Merge branch 'MaterialDesign'
@ -1,6 +1,8 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
VisualStudioVersion = 12.0.31101.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePassLib2Android", "KeePassLib2Android\KeePassLib2Android.csproj", "{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "keepass2android", "keepass2android\keepass2android.csproj", "{A6CF8A86-37C1-4197-80FE-519DE2C842F5}"
|
||||
@ -35,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MasterKeePlugin", "MasterKe
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZlibAndroid", "ZlibAndroid\ZlibAndroid.csproj", "{6C29A7E7-E016-4FC1-B1A0-DEE26AC711BB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaterialTest2", "MaterialTest2\MaterialTest2.csproj", "{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -333,6 +337,7 @@ Global
|
||||
{A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
|
||||
@ -378,6 +383,30 @@ Global
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{2F7CB5B4-AC2A-4790-B0F3-42E6C9A060D5} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3}
|
||||
|
@ -111,7 +111,7 @@ namespace KeePassLib
|
||||
/// <summary>
|
||||
/// Default number of master key encryption/transformation rounds (making dictionary attacks harder).
|
||||
/// </summary>
|
||||
public const ulong DefaultKeyEncryptionRounds = 6000;
|
||||
public const ulong DefaultKeyEncryptionRounds = 500000;
|
||||
|
||||
/// <summary>
|
||||
/// Default identifier string for the title field. Should not contain
|
||||
|
@ -7,9 +7,9 @@ namespace keepass2android
|
||||
{
|
||||
public interface IDrawableFactory
|
||||
{
|
||||
void AssignDrawableTo (ImageView iv, Resources res, PwDatabase db, PwIcon icon, PwUuid customIconId);
|
||||
void AssignDrawableTo (ImageView iv, Resources res, PwDatabase db, PwIcon icon, PwUuid customIconId, bool forGroup);
|
||||
|
||||
Drawable GetIconDrawable(Resources res, PwDatabase db, PwIcon icon, PwUuid customIconId);
|
||||
Drawable GetIconDrawable(Resources res, PwDatabase db, PwIcon icon, PwUuid customIconId, bool forGroup);
|
||||
|
||||
|
||||
void Clear();
|
||||
|
@ -55,8 +55,9 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="database\CheckDatabaseForChanges.cs" />
|
||||
<Compile Include="database\edit\DeleteMultipleItems.cs" />
|
||||
<Compile Include="database\edit\EditGroup.cs" />
|
||||
<Compile Include="database\edit\MoveElement.cs" />
|
||||
<Compile Include="database\edit\MoveElements.cs" />
|
||||
<Compile Include="database\KdbDatabaseFormat.cs" />
|
||||
<Compile Include="database\KdbxDatabaseFormat.cs" />
|
||||
<Compile Include="database\PwEntryOutput.cs" />
|
||||
|
@ -21,10 +21,10 @@ namespace keepass2android
|
||||
WriteDemanded = 2
|
||||
}
|
||||
|
||||
protected const int RequestCodeFileStorageSelectionForPrimarySelect = 983713;
|
||||
private const int RequestCodeFileStorageSelectionForCopyToWritableLocation = 983714;
|
||||
private const int RequestCodeFileFileBrowseForWritableLocation = 983715;
|
||||
private const int RequestCodeFileBrowseForOpen = 983716;
|
||||
protected const int RequestCodeFileStorageSelectionForPrimarySelect = 33713;
|
||||
private const int RequestCodeFileStorageSelectionForCopyToWritableLocation = 33714;
|
||||
private const int RequestCodeFileFileBrowseForWritableLocation = 33715;
|
||||
private const int RequestCodeFileBrowseForOpen = 33716;
|
||||
|
||||
|
||||
protected IOConnectionInfo _selectedIoc;
|
||||
|
@ -57,6 +57,8 @@ namespace keepass2android
|
||||
CopyFileRequiredForEditing,
|
||||
DuplicateUuidsError,
|
||||
DuplicateUuidsErrorAdditional,
|
||||
KdbBetaWarning
|
||||
KdbBetaWarning,
|
||||
DeletingItems,
|
||||
AskDeletePermanentlyItems
|
||||
}
|
||||
}
|
||||
|
@ -16,16 +16,19 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Interfaces;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
public class DeleteEntry : DeleteRunnable {
|
||||
|
||||
private readonly PwEntry _entry;
|
||||
private UiStringKey _statusMessage;
|
||||
|
||||
public DeleteEntry(Context ctx, IKp2aApp app, PwEntry entry, OnFinish finish):base(finish, app) {
|
||||
public DeleteEntry(Context ctx, IKp2aApp app, PwEntry entry, OnFinish finish):base(finish, app) {
|
||||
Ctx = ctx;
|
||||
Db = app.GetDb();
|
||||
_entry = entry;
|
||||
@ -48,76 +51,15 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
StatusLogger.UpdateMessage(UiStringKey.DeletingEntry);
|
||||
PwDatabase pd = Db.KpDatabase;
|
||||
protected override void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups)
|
||||
{
|
||||
DoDeleteEntry(_entry, touchedGroups);
|
||||
}
|
||||
|
||||
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
|
||||
|
||||
bool bUpdateGroupList = false;
|
||||
DateTime dtNow = DateTime.Now;
|
||||
PwEntry pe = _entry;
|
||||
PwGroup pgParent = pe.ParentGroup;
|
||||
if(pgParent != null)
|
||||
{
|
||||
pgParent.Entries.Remove(pe);
|
||||
//TODO check if RecycleBin is deleted
|
||||
//TODO no recycle bin in KDB
|
||||
|
||||
if ((DeletePermanently) || (!CanRecycle))
|
||||
{
|
||||
PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow);
|
||||
pd.DeletedObjects.Add(pdo);
|
||||
|
||||
_onFinishToRun = new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
// Mark parent dirty
|
||||
Db.Dirty.Add(pgParent);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's not bother recovering from a failure to save a deleted entry. It is too much work.
|
||||
App.LockDatabase(false);
|
||||
}
|
||||
}, OnFinishToRun);
|
||||
}
|
||||
else // Recycle
|
||||
{
|
||||
EnsureRecycleBinExists(ref pgRecycleBin, ref bUpdateGroupList);
|
||||
|
||||
pgRecycleBin.AddEntry(pe, true, true);
|
||||
pe.Touch(false);
|
||||
|
||||
_onFinishToRun = new ActionOnFinish( (success, message) =>
|
||||
{
|
||||
if ( success ) {
|
||||
// Mark previous parent dirty
|
||||
Db.Dirty.Add(pgParent);
|
||||
// Mark new parent dirty
|
||||
Db.Dirty.Add(pgRecycleBin);
|
||||
// mark root dirty if recycle bin was created
|
||||
if (bUpdateGroupList)
|
||||
Db.Dirty.Add(Db.Root);
|
||||
} else {
|
||||
// Let's not bother recovering from a failure to save a deleted entry. It is too much work.
|
||||
App.LockDatabase(false);
|
||||
}
|
||||
|
||||
}, OnFinishToRun);
|
||||
}
|
||||
}
|
||||
|
||||
// Commit database
|
||||
SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, false);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override UiStringKey StatusMessage
|
||||
{
|
||||
get { return UiStringKey.DeletingEntry; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
|
||||
@ -68,96 +69,15 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void Run() {
|
||||
StatusLogger.UpdateMessage(UiStringKey.DeletingGroup);
|
||||
//from KP Desktop
|
||||
PwGroup pg = _group;
|
||||
PwGroup pgParent = pg.ParentGroup;
|
||||
if(pgParent == null) return; // Can't remove virtual or root group
|
||||
|
||||
PwDatabase pd = Db.KpDatabase;
|
||||
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
|
||||
|
||||
pgParent.Groups.Remove(pg);
|
||||
|
||||
if ((DeletePermanently) || (!CanRecycle))
|
||||
{
|
||||
pg.DeleteAllObjects(pd);
|
||||
|
||||
PwDeletedObject pdo = new PwDeletedObject(pg.Uuid, DateTime.Now);
|
||||
pd.DeletedObjects.Add(pdo);
|
||||
_onFinishToRun = new AfterDeletePermanently(OnFinishToRun, App, _group);
|
||||
}
|
||||
else // Recycle
|
||||
{
|
||||
bool groupListUpdateRequired = false;
|
||||
EnsureRecycleBinExists(ref pgRecycleBin, ref groupListUpdateRequired);
|
||||
|
||||
pgRecycleBin.AddGroup(pg, true, true);
|
||||
pg.Touch(false);
|
||||
_onFinishToRun = new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if ( success ) {
|
||||
// Mark new parent (Recycle bin) dirty
|
||||
PwGroup parent = _group.ParentGroup;
|
||||
if ( parent != null ) {
|
||||
Db.Dirty.Add(parent);
|
||||
}
|
||||
//Mark old parent dirty:
|
||||
Db.Dirty.Add(pgParent);
|
||||
protected override void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups)
|
||||
{
|
||||
DoDeleteGroup(_group, touchedGroups, permanentlyDeletedGroups);
|
||||
}
|
||||
|
||||
// mark root dirty if recycle bin was created
|
||||
if (groupListUpdateRequired)
|
||||
Db.Dirty.Add(Db.Root);
|
||||
} else {
|
||||
// Let's not bother recovering from a failure to save a deleted group. It is too much work.
|
||||
App.LockDatabase(false);
|
||||
}
|
||||
}, OnFinishToRun);
|
||||
}
|
||||
|
||||
// Save
|
||||
SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, DontSave);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class AfterDeletePermanently : OnFinish {
|
||||
readonly IKp2aApp _app;
|
||||
|
||||
readonly PwGroup _group;
|
||||
|
||||
public AfterDeletePermanently(OnFinish finish, IKp2aApp app, PwGroup group):base(finish) {
|
||||
_app = app;
|
||||
_group = group;
|
||||
}
|
||||
|
||||
public override void Run() {
|
||||
if ( Success ) {
|
||||
// Remove from group global
|
||||
_app.GetDb().Groups.Remove(_group.Uuid);
|
||||
|
||||
// Remove group from the dirty global (if it is present), not a big deal if this fails (doesn't throw)
|
||||
_app.GetDb().Dirty.Remove(_group);
|
||||
|
||||
// Mark parent dirty
|
||||
PwGroup parent = _group.ParentGroup;
|
||||
if ( parent != null ) {
|
||||
_app.GetDb().Dirty.Add(parent);
|
||||
}
|
||||
} else {
|
||||
// Let's not bother recovering from a failure to save a deleted group. It is too much work.
|
||||
_app.LockDatabase(false);
|
||||
}
|
||||
|
||||
base.Run();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public override UiStringKey StatusMessage
|
||||
{
|
||||
get { return UiStringKey.DeletingGroup; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
79
src/Kp2aBusinessLogic/database/edit/DeleteMultipleItems.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Interfaces;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
public class DeleteMultipleItems : DeleteRunnable
|
||||
{
|
||||
private readonly List<IStructureItem> _elementsToDelete;
|
||||
private readonly bool _canRecycle;
|
||||
|
||||
public DeleteMultipleItems(Context ctx, Database db, List<IStructureItem> elementsToDelete, OnFinish finish, IKp2aApp app)
|
||||
: base(finish, app)
|
||||
{
|
||||
_elementsToDelete = elementsToDelete;
|
||||
SetMembers(ctx, db);
|
||||
|
||||
//determine once. The property is queried for each delete operation, but might return false
|
||||
//after one entry/group is deleted (and thus in recycle bin and thus can't be recycled anymore)
|
||||
_canRecycle = DetermineCanRecycle();
|
||||
}
|
||||
|
||||
private bool DetermineCanRecycle()
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "CanRecycle?");
|
||||
if (!App.GetDb().DatabaseFormat.CanRecycle)
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "CanRecycle? No because of DB format.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (_elementsToDelete.OfType<PwGroup>().Any(g => !CanRecycleGroup(g)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_elementsToDelete.OfType<PwEntry>().Any(e => !CanRecycleGroup(e.ParentGroup)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Android.Util.Log.Debug("KP2A", "CanRecycle? Yes.");
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CanRecycle
|
||||
{
|
||||
get { return _canRecycle; }
|
||||
}
|
||||
|
||||
protected override UiStringKey QuestionsResourceId
|
||||
{
|
||||
get { return UiStringKey.AskDeletePermanentlyItems; }
|
||||
}
|
||||
|
||||
protected override void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups)
|
||||
{
|
||||
foreach (var g in _elementsToDelete.OfType<PwGroup>())
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "Deleting " + g.Name);
|
||||
DoDeleteGroup(g, touchedGroups, permanentlyDeletedGroups);
|
||||
|
||||
}
|
||||
|
||||
foreach (var e in _elementsToDelete.OfType<PwEntry>())
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "Deleting " + e.Strings.ReadSafe(PwDefs.TitleField));
|
||||
DoDeleteEntry(e, touchedGroups);
|
||||
}
|
||||
}
|
||||
|
||||
public override UiStringKey StatusMessage
|
||||
{
|
||||
get { return UiStringKey.DeletingItems; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
|
||||
@ -5,12 +7,13 @@ namespace keepass2android
|
||||
{
|
||||
public abstract class DeleteRunnable : RunnableOnFinish
|
||||
{
|
||||
protected DeleteRunnable(OnFinish finish, IKp2aApp app):base(finish)
|
||||
protected DeleteRunnable(OnFinish finish, IKp2aApp app)
|
||||
: base(finish)
|
||||
{
|
||||
App = app;
|
||||
App = app;
|
||||
}
|
||||
|
||||
protected IKp2aApp App;
|
||||
protected IKp2aApp App;
|
||||
|
||||
protected Database Db;
|
||||
|
||||
@ -22,9 +25,9 @@ namespace keepass2android
|
||||
Db = db;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private bool _deletePermanently = true;
|
||||
|
||||
|
||||
public bool DeletePermanently
|
||||
{
|
||||
get
|
||||
@ -44,52 +47,61 @@ namespace keepass2android
|
||||
|
||||
protected bool CanRecycleGroup(PwGroup pgParent)
|
||||
{
|
||||
bool bShiftPressed = false;
|
||||
PwDatabase pd = Db.KpDatabase;
|
||||
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
|
||||
bool bPermanent = false;
|
||||
if (pgParent != null)
|
||||
{
|
||||
if (pd.RecycleBinEnabled == false)
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "CanRecycle? No, RecycleBinIsNotEnabled");
|
||||
bPermanent = true;
|
||||
else if (bShiftPressed)
|
||||
bPermanent = true;
|
||||
}
|
||||
|
||||
else if (pgRecycleBin == null)
|
||||
{
|
||||
} // Recycle
|
||||
else if (pgParent == pgRecycleBin)
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "CanRecycle? No, Can't recycle RecycleBin");
|
||||
bPermanent = true;
|
||||
}
|
||||
|
||||
else if (pgParent.IsContainedIn(pgRecycleBin))
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "CanRecycle? No, "+pgParent.Name+" is in RecycleBin");
|
||||
bPermanent = true;
|
||||
}
|
||||
|
||||
}
|
||||
return !bPermanent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected void EnsureRecycleBinExists(ref PwGroup pgRecycleBin,
|
||||
ref bool bGroupListUpdateRequired)
|
||||
ref bool bGroupListUpdateRequired)
|
||||
{
|
||||
if ((Db == null) || (Db.KpDatabase == null)) { return; }
|
||||
|
||||
if(pgRecycleBin == Db.KpDatabase.RootGroup)
|
||||
|
||||
if (pgRecycleBin == Db.KpDatabase.RootGroup)
|
||||
{
|
||||
pgRecycleBin = null;
|
||||
}
|
||||
|
||||
if(pgRecycleBin == null)
|
||||
|
||||
if (pgRecycleBin == null)
|
||||
{
|
||||
pgRecycleBin = new PwGroup(true, true, App.GetResourceString(UiStringKey.RecycleBin),
|
||||
PwIcon.TrashBin)
|
||||
PwIcon.TrashBin)
|
||||
{
|
||||
EnableAutoType = false,
|
||||
EnableSearching = false,
|
||||
EnableAutoType = false,
|
||||
EnableSearching = false,
|
||||
IsExpanded = false
|
||||
};
|
||||
|
||||
Db.KpDatabase.RootGroup.AddGroup(pgRecycleBin, true);
|
||||
Db.Groups[pgRecycleBin.Uuid] = pgRecycleBin;
|
||||
Db.KpDatabase.RecycleBinUuid = pgRecycleBin.Uuid;
|
||||
|
||||
|
||||
bGroupListUpdateRequired = true;
|
||||
}
|
||||
else { System.Diagnostics.Debug.Assert(pgRecycleBin.Uuid.Equals(Db.KpDatabase.RecycleBinUuid)); }
|
||||
@ -99,36 +111,155 @@ namespace keepass2android
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (CanRecycle)
|
||||
{
|
||||
App.AskYesNoCancel(UiStringKey.AskDeletePermanently_title,
|
||||
QuestionsResourceId,
|
||||
(dlgSender, dlgEvt) =>
|
||||
{
|
||||
DeletePermanently = true;
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
},
|
||||
(dlgSender, dlgEvt) => {
|
||||
DeletePermanently = false;
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
},
|
||||
(dlgSender, dlgEvt) => {},
|
||||
Ctx);
|
||||
App.AskYesNoCancel(UiStringKey.AskDeletePermanently_title,
|
||||
QuestionsResourceId,
|
||||
(dlgSender, dlgEvt) =>
|
||||
{
|
||||
DeletePermanently = true;
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
},
|
||||
(dlgSender, dlgEvt) =>
|
||||
{
|
||||
DeletePermanently = false;
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
},
|
||||
(dlgSender, dlgEvt) => { },
|
||||
Ctx);
|
||||
|
||||
|
||||
|
||||
} else
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
}
|
||||
}
|
||||
|
||||
protected void DoDeleteEntry(PwEntry pe, List<PwGroup> touchedGroups)
|
||||
{
|
||||
PwDatabase pd = Db.KpDatabase;
|
||||
|
||||
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
|
||||
|
||||
bool bUpdateGroupList = false;
|
||||
DateTime dtNow = DateTime.Now;
|
||||
|
||||
PwGroup pgParent = pe.ParentGroup;
|
||||
if (pgParent != null)
|
||||
{
|
||||
pgParent.Entries.Remove(pe);
|
||||
//TODO check if RecycleBin is deleted
|
||||
//TODO no recycle bin in KDB
|
||||
|
||||
if ((DeletePermanently) || (!CanRecycle))
|
||||
{
|
||||
PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow);
|
||||
pd.DeletedObjects.Add(pdo);
|
||||
touchedGroups.Add(pgParent);
|
||||
}
|
||||
else // Recycle
|
||||
{
|
||||
EnsureRecycleBinExists(ref pgRecycleBin, ref bUpdateGroupList);
|
||||
|
||||
pgRecycleBin.AddEntry(pe, true, true);
|
||||
pe.Touch(false);
|
||||
|
||||
touchedGroups.Add(pgParent);
|
||||
// Mark new parent dirty
|
||||
touchedGroups.Add(pgRecycleBin);
|
||||
// mark root dirty if recycle bin was created
|
||||
touchedGroups.Add(Db.Root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
StatusLogger.UpdateMessage(StatusMessage);
|
||||
|
||||
List<PwGroup> touchedGroups = new List<PwGroup>();
|
||||
List<PwGroup> permanentlyDeletedGroups = new List<PwGroup>();
|
||||
Android.Util.Log.Debug("KP2A", "Calling PerformDelete..");
|
||||
PerformDelete(touchedGroups, permanentlyDeletedGroups);
|
||||
|
||||
_onFinishToRun = new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
foreach (var g in touchedGroups)
|
||||
Db.Dirty.Add(g);
|
||||
foreach (var g in permanentlyDeletedGroups)
|
||||
{
|
||||
//remove groups from global lists if present there
|
||||
Db.Dirty.Remove(g);
|
||||
Db.Groups.Remove(g.Uuid);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's not bother recovering from a failure to save. It is too much work.
|
||||
App.LockDatabase(false);
|
||||
}
|
||||
}, OnFinishToRun);
|
||||
|
||||
// Commit database
|
||||
SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, false);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected abstract void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups);
|
||||
|
||||
public abstract UiStringKey StatusMessage { get; }
|
||||
|
||||
protected bool DoDeleteGroup(PwGroup pg, List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups)
|
||||
{
|
||||
PwGroup pgParent = pg.ParentGroup;
|
||||
if (pgParent == null) return false;
|
||||
|
||||
PwDatabase pd = Db.KpDatabase;
|
||||
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
|
||||
|
||||
pgParent.Groups.Remove(pg);
|
||||
touchedGroups.Add(pgParent);
|
||||
if ((DeletePermanently) || (!CanRecycle))
|
||||
{
|
||||
pg.DeleteAllObjects(pd);
|
||||
|
||||
PwDeletedObject pdo = new PwDeletedObject(pg.Uuid, DateTime.Now);
|
||||
pd.DeletedObjects.Add(pdo);
|
||||
|
||||
|
||||
permanentlyDeletedGroups.Add(pg);
|
||||
|
||||
}
|
||||
else // Recycle
|
||||
{
|
||||
bool groupListUpdateRequired = false;
|
||||
EnsureRecycleBinExists(ref pgRecycleBin, ref groupListUpdateRequired);
|
||||
|
||||
pgRecycleBin.AddGroup(pg, true, true);
|
||||
pg.Touch(false);
|
||||
// Mark new parent (Recycle bin) touched
|
||||
touchedGroups.Add(pg.ParentGroup);
|
||||
// mark root touched if recycle bin was created
|
||||
if (groupListUpdateRequired)
|
||||
touchedGroups.Add(Db.Root);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Interfaces;
|
||||
|
||||
namespace keepass2android.database.edit
|
||||
{
|
||||
public class MoveElement: RunnableOnFinish
|
||||
{
|
||||
private readonly IStructureItem _elementToMove;
|
||||
private readonly PwGroup _targetGroup;
|
||||
private readonly Context _ctx;
|
||||
private readonly IKp2aApp _app;
|
||||
|
||||
public MoveElement(IStructureItem elementToMove, PwGroup targetGroup, Context ctx, IKp2aApp app, OnFinish finish) : base(finish)
|
||||
{
|
||||
_elementToMove = elementToMove;
|
||||
_targetGroup = targetGroup;
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
|
||||
_app.GetDb().Dirty.Add(_elementToMove.ParentGroup);
|
||||
|
||||
PwGroup pgParent = _elementToMove.ParentGroup;
|
||||
if (pgParent != _targetGroup)
|
||||
{
|
||||
if (pgParent != null) // Remove from parent
|
||||
{
|
||||
PwEntry entry = _elementToMove as PwEntry;
|
||||
if (entry != null)
|
||||
{
|
||||
pgParent.Entries.Remove(entry);
|
||||
_targetGroup.AddEntry(entry, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
PwGroup group = (PwGroup)_elementToMove;
|
||||
if ((_targetGroup == group) || (_targetGroup.IsContainedIn(group)))
|
||||
{
|
||||
Finish(false, _app.GetResourceString(UiStringKey.CannotMoveGroupHere));
|
||||
return;
|
||||
}
|
||||
pgParent.Groups.Remove(group);
|
||||
_targetGroup.AddGroup(group, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onFinishToRun = new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!success)
|
||||
{ // Let's not bother recovering from a failure.
|
||||
_app.LockDatabase(false);
|
||||
}
|
||||
}, OnFinishToRun);
|
||||
|
||||
// Save
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, false);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
}
|
||||
}
|
||||
}
|
99
src/Kp2aBusinessLogic/database/edit/MoveElements.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Interfaces;
|
||||
|
||||
namespace keepass2android.database.edit
|
||||
{
|
||||
public class MoveElements: RunnableOnFinish
|
||||
{
|
||||
private readonly List<IStructureItem> _elementsToMove;
|
||||
private readonly PwGroup _targetGroup;
|
||||
private readonly Context _ctx;
|
||||
private readonly IKp2aApp _app;
|
||||
|
||||
public MoveElements(List<IStructureItem> elementsToMove, PwGroup targetGroup, Context ctx, IKp2aApp app, OnFinish finish) : base(finish)
|
||||
{
|
||||
_elementsToMove = elementsToMove;
|
||||
_targetGroup = targetGroup;
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
//check if we will run into problems. Then finish with error before we start doing anything.
|
||||
foreach (var _elementToMove in _elementsToMove)
|
||||
{
|
||||
PwGroup pgParent = _elementToMove.ParentGroup;
|
||||
if (pgParent != _targetGroup)
|
||||
{
|
||||
if (pgParent != null)
|
||||
{
|
||||
PwGroup group = _elementToMove as PwGroup;
|
||||
if (group != null)
|
||||
{
|
||||
if ((_targetGroup == group) || (_targetGroup.IsContainedIn(group)))
|
||||
{
|
||||
Finish(false, _app.GetResourceString(UiStringKey.CannotMoveGroupHere));
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
foreach (var elementToMove in _elementsToMove)
|
||||
{
|
||||
|
||||
_app.GetDb().Dirty.Add(elementToMove.ParentGroup);
|
||||
|
||||
PwGroup pgParent = elementToMove.ParentGroup;
|
||||
if (pgParent != _targetGroup)
|
||||
{
|
||||
if (pgParent != null) // Remove from parent
|
||||
{
|
||||
PwEntry entry = elementToMove as PwEntry;
|
||||
if (entry != null)
|
||||
{
|
||||
pgParent.Entries.Remove(entry);
|
||||
_targetGroup.AddEntry(entry, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
PwGroup group = (PwGroup)elementToMove;
|
||||
if ((_targetGroup == group) || (_targetGroup.IsContainedIn(group)))
|
||||
{
|
||||
Finish(false, _app.GetResourceString(UiStringKey.CannotMoveGroupHere));
|
||||
return;
|
||||
}
|
||||
pgParent.Groups.Remove(group);
|
||||
_targetGroup.AddGroup(group, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
_onFinishToRun = new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!success)
|
||||
{ // Let's not bother recovering from a failure.
|
||||
_app.LockDatabase(false);
|
||||
}
|
||||
}, OnFinishToRun);
|
||||
|
||||
// Save
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, false);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
}
|
||||
}
|
||||
}
|
@ -9,12 +9,12 @@ namespace Kp2aUnitTests
|
||||
{
|
||||
internal class TestDrawableFactory : IDrawableFactory
|
||||
{
|
||||
public void AssignDrawableTo(ImageView iv, Resources res, PwDatabase db, PwIcon icon, PwUuid customIconId)
|
||||
public void AssignDrawableTo(ImageView iv, Resources res, PwDatabase db, PwIcon icon, PwUuid customIconId, bool forGroup)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Drawable GetIconDrawable(Resources res, PwDatabase db, PwIcon icon, PwUuid customIconId)
|
||||
public Drawable GetIconDrawable(Resources res, PwDatabase db, PwIcon icon, PwUuid customIconId, bool forGroup)
|
||||
{
|
||||
return res.GetDrawable(Resource.Drawable.Icon);
|
||||
}
|
||||
|
@ -71,6 +71,10 @@ namespace MasterKeePlugin
|
||||
_showPassword = !_showPassword;
|
||||
MakePasswordMaskedOrVisible();
|
||||
};
|
||||
Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop;
|
||||
Android.Graphics.Color color = new Android.Graphics.Color (224, 224, 224);
|
||||
btnTogglePassword.SetColorFilter (color, mMode);
|
||||
|
||||
|
||||
FindViewById(Resource.Id.button_ok).Click += delegate(object sender, EventArgs args)
|
||||
{
|
||||
|
10
src/MaterialTest/Resources/layout/toolbar.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:local="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
local:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
local:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
|
10
src/MaterialTest/Resources/layout/toolbar2.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:local="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
local:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
local:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
|
14
src/MaterialTest/Resources/values-v21/styles.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<resources>
|
||||
<!--
|
||||
Base application theme for API 21+. This theme replaces
|
||||
MyTheme from resources/values/styles.xml on API 21+ devices.
|
||||
-->
|
||||
<style name="MyTheme" parent="MyTheme.Base">
|
||||
<item name="android:windowContentTransitions">true</item>
|
||||
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
||||
<item name="android:windowAllowReturnTransitionOverlap">true</item>
|
||||
<item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
|
||||
<item name="android:windowSharedElementExitTransition">@android:transition/move</item>
|
||||
</style>
|
||||
</resources>
|
22
src/MaterialTest/Resources/values/styles.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<resources>
|
||||
<style name="MyTheme" parent="MyTheme.Base">
|
||||
</style>
|
||||
<!-- Base theme applied no matter what API -->
|
||||
<style name="MyTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<!--We will be using the toolbar so no need to show ActionBar-->
|
||||
<item name="windowActionBar">false</item>
|
||||
<!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette-->
|
||||
<!-- colorPrimary is used for the default action bar background -->
|
||||
<item name="colorPrimary">#2196F3</item>
|
||||
<!-- colorPrimaryDark is used for the status bar -->
|
||||
<item name="colorPrimaryDark">#1976D2</item>
|
||||
<!-- colorAccent is used as the default value for colorControlActivated
|
||||
which is used to tint widgets -->
|
||||
<item name="colorAccent">#FF4081</item>
|
||||
<!-- You can also set colorControlNormal, colorControlActivated
|
||||
colorControlHighlight and colorSwitchThumbNormal. -->
|
||||
</style>
|
||||
</resources>
|
||||
|
7
src/MaterialTest/packages.config
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Xamarin.Android.Support.v4" version="21.0.3.0" targetFramework="MonoAndroid22" />
|
||||
<package id="Xamarin.Android.Support.v7.AppCompat" version="21.0.3.0" targetFramework="MonoAndroid50" />
|
||||
<package id="Xamarin.Android.Support.v7.MediaRouter" version="21.0.3.0" targetFramework="MonoAndroid50" />
|
||||
<package id="Xamarin.GooglePlayServices" version="22.0.0.0" targetFramework="MonoAndroid50" />
|
||||
</packages>
|
19
src/MaterialTest2/Assets/AboutAssets.txt
Normal file
@ -0,0 +1,19 @@
|
||||
Any raw assets you want to be deployed with your application can be placed in
|
||||
this directory (and child directories) and given a Build Action of "AndroidAsset".
|
||||
|
||||
These files will be deployed with your package and will be accessible using Android's
|
||||
AssetManager, like this:
|
||||
|
||||
public class ReadAsset : Activity
|
||||
{
|
||||
protected override void OnCreate (Bundle bundle)
|
||||
{
|
||||
base.OnCreate (bundle);
|
||||
|
||||
InputStream input = Assets.Open ("my_asset.txt");
|
||||
}
|
||||
}
|
||||
|
||||
Additionally, some Android functions will automatically load asset files:
|
||||
|
||||
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
|
214
src/MaterialTest2/MainActivity.cs
Normal file
@ -0,0 +1,214 @@
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.Runtime;
|
||||
using Android.Support.V4.View;
|
||||
using Android.Support.V4.Widget;
|
||||
using Android.Support.V7.App;
|
||||
|
||||
using Android.Support.Design.Widget;
|
||||
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.OS;
|
||||
using Android.Util;
|
||||
using Toolbar = Android.Support.V7.Widget.Toolbar;
|
||||
namespace MaterialTest2
|
||||
{
|
||||
public class MyDrawerLayout : Android.Support.V4.Widget.DrawerLayout
|
||||
{
|
||||
private bool _fitsSystemWindows;
|
||||
|
||||
protected MyDrawerLayout(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
|
||||
public MyDrawerLayout(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
|
||||
{
|
||||
}
|
||||
|
||||
public MyDrawerLayout(Context context, IAttributeSet attrs) : base(context, attrs)
|
||||
{
|
||||
}
|
||||
|
||||
public MyDrawerLayout(Context context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
private int[] mInsets = new int[4];
|
||||
|
||||
protected override bool FitSystemWindows(Rect insets)
|
||||
{
|
||||
if (Build.VERSION.SdkInt >= Build.VERSION_CODES.Kitkat)
|
||||
{
|
||||
// Intentionally do not modify the bottom inset. For some reason,
|
||||
// if the bottom inset is modified, window resizing stops working.
|
||||
// TODO: Figure out why.
|
||||
|
||||
mInsets[0] = insets.Left;
|
||||
mInsets[1] = insets.Top;
|
||||
mInsets[2] = insets.Right;
|
||||
|
||||
insets.Left = 0;
|
||||
insets.Top = 0;
|
||||
insets.Right = 0;
|
||||
}
|
||||
|
||||
return base.FitSystemWindows(insets);
|
||||
|
||||
}
|
||||
public int[] GetInsets()
|
||||
{
|
||||
return mInsets;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Activity(Theme="@style/MyTheme", Label = "MaterialTest", MainLauncher = false, Icon = "@drawable/icon", WindowSoftInputMode = SoftInput.AdjustResize)]
|
||||
public class MainActivity : AppCompatActivity
|
||||
{
|
||||
int count = 1;
|
||||
|
||||
private DrawerLayout mDrawerLayout;
|
||||
//private RecyclerView mDrawerList;
|
||||
private ActionBarDrawerToggle mDrawerToggle;
|
||||
|
||||
private string mDrawerTitle;
|
||||
|
||||
public override bool OnCreateOptionsMenu(IMenu menu)
|
||||
{
|
||||
MenuInflater.Inflate(Resource.Menu.menu_password, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool OnOptionsItemSelected(IMenuItem item)
|
||||
{
|
||||
switch (item.ItemId)
|
||||
{
|
||||
case Android.Resource.Id.Home:
|
||||
mDrawerLayout.OpenDrawer(Android.Support.V4.View.GravityCompat.Start);
|
||||
return true;
|
||||
}
|
||||
return base.OnOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
// Set our view from the "main" layout resource
|
||||
SetContentView(Resource.Layout.Main);
|
||||
|
||||
mDrawerTitle = this.Title;
|
||||
//mPlanetTitles = this.Resources.GetStringArray (Resource.Array.planets_array);
|
||||
mDrawerLayout = FindViewById<DrawerLayout>(Resource.Id.drawer_layout);
|
||||
//mDrawerList = FindViewById<RecyclerView> (Resource.Id.left_drawer);
|
||||
|
||||
|
||||
//mDrawerLayout.SetDrawerShadow (Resource.Drawable.drawer_shadow, GravityCompat.Start);
|
||||
// improve performance by indicating the list if fixed size.
|
||||
//mDrawerList.HasFixedSize = true;
|
||||
//mDrawerList.SetLayoutManager (new LinearLayoutManager (this));
|
||||
NavigationView nv;
|
||||
// set up the drawer's list view with items and click listener
|
||||
//mDrawerList.SetAdapter (new PlanetAdapter (mPlanetTitles, this));
|
||||
// enable ActionBar app icon to behave as action to toggle nav drawer
|
||||
|
||||
// ActionBarDrawerToggle ties together the the proper interactions
|
||||
// between the sliding drawer and the action bar app icon
|
||||
|
||||
mDrawerToggle = new MyActionBarDrawerToggle (this, mDrawerLayout,
|
||||
Resource.Drawable.abc_ic_menu_copy_mtrl_am_alpha,
|
||||
Resource.String.drawer_open,
|
||||
Resource.String.drawer_close);
|
||||
|
||||
mDrawerLayout.SetDrawerListener (mDrawerToggle);
|
||||
|
||||
// Get our button from the layout resource,
|
||||
// and attach an event to it
|
||||
Button button = FindViewById<Button>(Resource.Id.MyButton);
|
||||
|
||||
button.Click += delegate { button.Text = string.Format("{0} clicks!", count++);
|
||||
FindViewById(Resource.Id.MyButton).SetMinimumHeight(30*count);
|
||||
};
|
||||
|
||||
|
||||
FindViewById<ImageButton>(Resource.Id.eyebutton).Click += delegate(object sender, EventArgs args)
|
||||
{
|
||||
Snackbar.Make(FindViewById<ImageButton>(Resource.Id.eyebutton), "Here's a snackbar!", Snackbar.LengthLong).SetAction("Action",
|
||||
new ClickListener(v =>
|
||||
{
|
||||
Console.WriteLine("Action handler");
|
||||
})).Show();
|
||||
};
|
||||
|
||||
var toolbar = FindViewById<Toolbar> (Resource.Id.toolbar);
|
||||
//SupportActionBar.SetBackgroundDrawable(GetDrawable(Resource.Drawable.ic_keepass2android));
|
||||
//Toolbar will now take on default Action Bar characteristics
|
||||
SetSupportActionBar (toolbar);
|
||||
//You can now use and reference the ActionBar
|
||||
//SupportActionBar.Title = "Hello from Toolbar";
|
||||
|
||||
var collapsingToolbar = FindViewById<CollapsingToolbarLayout> (Resource.Id.collapsing_toolbar);
|
||||
collapsingToolbar.SetTitle ("Unlock Database");
|
||||
|
||||
//SupportActionBar.SetHomeAsUpIndicator (Resource.Drawable.ic_menu);
|
||||
//SupportActionBar.SetDisplayHomeAsUpEnabled (true);
|
||||
|
||||
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
SupportActionBar.SetHomeButtonEnabled(true);
|
||||
mDrawerToggle.SyncState();
|
||||
|
||||
var fab = FindViewById<FloatingActionButton> (Resource.Id.fab);
|
||||
fab.Click += (sender, e) => {
|
||||
Snackbar.Make (fab, "Here's a snackbar!", Snackbar.LengthLong).SetAction ("Action",
|
||||
new ClickListener (v => {
|
||||
Console.WriteLine ("Action handler");
|
||||
})).Show ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public class ClickListener : Java.Lang.Object, View.IOnClickListener
|
||||
{
|
||||
public ClickListener (Action<View> handler)
|
||||
{
|
||||
Handler = handler;
|
||||
}
|
||||
|
||||
public Action<View> Handler { get; set; }
|
||||
|
||||
public void OnClick (View v)
|
||||
{
|
||||
var h = Handler;
|
||||
if (h != null)
|
||||
h (v);
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyActionBarDrawerToggle : ActionBarDrawerToggle
|
||||
{
|
||||
MainActivity owner;
|
||||
|
||||
public MyActionBarDrawerToggle(MainActivity activity, DrawerLayout layout, int imgRes, int openRes, int closeRes)
|
||||
: base(activity, layout, openRes, closeRes)
|
||||
{
|
||||
owner = activity;
|
||||
}
|
||||
|
||||
public override void OnDrawerClosed(View drawerView)
|
||||
{
|
||||
owner.SupportActionBar.Title = owner.Title;
|
||||
owner.InvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
public override void OnDrawerOpened(View drawerView)
|
||||
{
|
||||
owner.SupportActionBar.Title = owner.mDrawerTitle;
|
||||
owner.InvalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
267
src/MaterialTest2/MaterialTest2.csproj
Normal file
@ -0,0 +1,267 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<ProjectGuid>{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>MaterialTest2</RootNamespace>
|
||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
|
||||
<AndroidApplication>True</AndroidApplication>
|
||||
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
|
||||
<AndroidResgenClass>Resource</AndroidResgenClass>
|
||||
<AssemblyName>MaterialTest2</AssemblyName>
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="Xamarin.Android.Support.v4">
|
||||
<HintPath>..\packages\Xamarin.Android.Support.v4.22.2.1.0\lib\MonoAndroid403\Xamarin.Android.Support.v4.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.AppCompat">
|
||||
<HintPath>..\packages\Xamarin.Android.Support.v7.AppCompat.22.2.1.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.AppCompat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Design">
|
||||
<HintPath>..\packages\Xamarin.Android.Support.Design.22.2.1.0\lib\MonoAndroid403\Xamarin.Android.Support.Design.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileStorageSelect.cs" />
|
||||
<Compile Include="GroupActivity.cs" />
|
||||
<Compile Include="GroupEditActivity.cs" />
|
||||
<Compile Include="GroupListFragment.cs" />
|
||||
<Compile Include="MainActivity.cs" />
|
||||
<Compile Include="PwEntryView.cs" />
|
||||
<Compile Include="PwGroupView.cs" />
|
||||
<Compile Include="Resources\Resource.designer.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SearchProvider.cs" />
|
||||
<Compile Include="SplashActivity.cs" />
|
||||
<Compile Include="EntryEditActivity2.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
<None Include="Assets\AboutAssets.txt" />
|
||||
<None Include="Properties\AndroidManifest.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<AndroidResource Include="Resources\drawable\group_icon_bkg_drawable.xml" />
|
||||
<AndroidResource Include="Resources\layout\my_list_item.axml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\values\dimens.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\layout\nav_header.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_dashboard.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_discuss.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_done.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_event.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_forum.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_headset.png" />
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_menu.png" />
|
||||
<AndroidResource Include="Resources\layout\Splash.axml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\layout\filestorageselect.xml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\layout\entry_edit.axml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\layout\group.axml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\layout\generate_password.axml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
</AndroidResource>
|
||||
<None Include="packages.config" />
|
||||
<AndroidResource Include="Resources\drawable\search_dropdown_light.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\Main.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\values\Strings.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\drawable\Icon.png" />
|
||||
<AndroidResource Include="Resources\layout\toolbar2.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\values\styles.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\values-v21\styles.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\drawer_shadow.9.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\ic_keepass2android.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\toolbar_bg.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\list_selector.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\values\colors.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources\color\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\menu\menu_password.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\ic_action_eye_open.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\splashlogo.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\createnewdb.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\opendb.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\splash_bg_selector.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\splash_button_bg.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\storagetype_grid_bg.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\storagetype_button_bg.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\group_list_entry.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\entry_list_entry.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\grouplistitem_selector.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\checked_drawable.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\listitem_unchecked_drawable.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\colortest.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\entry_icon_bkg_drawable.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\checkmark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\rightarrow.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\grouplistfragment.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<XamarinComponentReference Include="xamandroidsupportdesign">
|
||||
<Version>22.2.0.0</Version>
|
||||
<Visible>False</Visible>
|
||||
</XamarinComponentReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\group_edit.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_attachments.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_comments.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_created.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_expires.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_extras.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_group.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_modified.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_password.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_tags.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_url.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\ic_entry_username.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\menu\menu_entryedit.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\xml\searchable_mattest.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
</Project>
|
5
src/MaterialTest2/Properties/AndroidManifest.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="MaterialTest2.MaterialTest2" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
|
||||
<application android:label="MaterialTest2" android:icon="@drawable/ic_discuss"></application>
|
||||
</manifest>
|
28
src/MaterialTest2/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Android.App;
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle("MaterialTest2")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("Philipp")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||
|
||||
[assembly: AssemblyVersion("1.0.0")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
||||
|
44
src/MaterialTest2/Resources/AboutResources.txt
Normal file
@ -0,0 +1,44 @@
|
||||
Images, layout descriptions, binary blobs and string dictionaries can be included
|
||||
in your application as resource files. Various Android APIs are designed to
|
||||
operate on the resource IDs instead of dealing with images, strings or binary blobs
|
||||
directly.
|
||||
|
||||
For example, a sample Android app that contains a user interface layout (main.axml),
|
||||
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
|
||||
would keep its resources in the "Resources" directory of the application:
|
||||
|
||||
Resources/
|
||||
drawable/
|
||||
icon.png
|
||||
|
||||
layout/
|
||||
main.axml
|
||||
|
||||
values/
|
||||
strings.xml
|
||||
|
||||
In order to get the build system to recognize Android resources, set the build action to
|
||||
"AndroidResource". The native Android APIs do not operate directly with filenames, but
|
||||
instead operate on resource IDs. When you compile an Android application that uses resources,
|
||||
the build system will package the resources for distribution and generate a class called "R"
|
||||
(this is an Android convention) that contains the tokens for each one of the resources
|
||||
included. For example, for the above Resources layout, this is what the R class would expose:
|
||||
|
||||
public class R {
|
||||
public class drawable {
|
||||
public const int icon = 0x123;
|
||||
}
|
||||
|
||||
public class layout {
|
||||
public const int main = 0x456;
|
||||
}
|
||||
|
||||
public class strings {
|
||||
public const int first_string = 0xabc;
|
||||
public const int second_string = 0xbcd;
|
||||
}
|
||||
}
|
||||
|
||||
You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
|
||||
to reference the layout/main.axml file, or R.strings.first_string to reference the first
|
||||
string in the dictionary file values/strings.xml.
|
BIN
src/MaterialTest2/Resources/drawable-xhdpi/ic_dashboard.png
Normal file
After Width: | Height: | Size: 132 B |
BIN
src/MaterialTest2/Resources/drawable-xhdpi/ic_discuss.png
Normal file
After Width: | Height: | Size: 247 B |
BIN
src/MaterialTest2/Resources/drawable-xhdpi/ic_done.png
Normal file
After Width: | Height: | Size: 297 B |
BIN
src/MaterialTest2/Resources/drawable-xhdpi/ic_event.png
Normal file
After Width: | Height: | Size: 225 B |
BIN
src/MaterialTest2/Resources/drawable-xhdpi/ic_forum.png
Normal file
After Width: | Height: | Size: 226 B |
BIN
src/MaterialTest2/Resources/drawable-xhdpi/ic_headset.png
Normal file
After Width: | Height: | Size: 488 B |
BIN
src/MaterialTest2/Resources/drawable-xhdpi/ic_menu.png
Normal file
After Width: | Height: | Size: 135 B |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2013 The Android Open Source Project
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#F0FFFFFF"/>
|
||||
<stroke android:width="1dp" color="#A00080FF"/>
|
||||
<padding android:left="5dp" android:top="0dp"
|
||||
android:right="5dp" android:bottom="1dp" />
|
||||
</shape>
|
69
src/MaterialTest2/Resources/layout/Mainold.axml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:local="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/drawerLayout"
|
||||
android:layout_height="match_parent">
|
||||
<!-- activity view -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<include
|
||||
android:id="@+id/toolbar2"
|
||||
layout="@layout/toolbar2" />
|
||||
<ScrollView xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:id="@+id/main_content">
|
||||
<Button
|
||||
android:id="@+id/MyButton"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/Hello" />
|
||||
<CheckBox
|
||||
android:text="CheckBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/checkBox1" />
|
||||
<ImageView
|
||||
android:id="@+id/imglogo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_keepass2android" />
|
||||
<EditText
|
||||
android:inputType="textEmailAddress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/editText1" />
|
||||
<ListView
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:choiceMode="singleChoice" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
<!-- navigation drawer -->
|
||||
<RelativeLayout
|
||||
android:layout_gravity="left|start"
|
||||
android:layout_width="match_parent"
|
||||
android:background="#fff"
|
||||
android:layout_height="match_parent">
|
||||
<Button
|
||||
android:id="@+id/MyButton2"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/Hello" />
|
||||
</RelativeLayout>
|
||||
</android.support.v4.widget.DrawerLayout>
|
33
src/MaterialTest2/Resources/layout/nav_header.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2015 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="192dp"
|
||||
android:background="?attr/colorAccent"
|
||||
android:padding="16dp"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark"
|
||||
android:orientation="vertical"
|
||||
android:gravity="bottom">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Username"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
|
||||
|
||||
</LinearLayout>
|
14
src/MaterialTest2/Resources/layout/toolbar2.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:local="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="bottom"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:background="@drawable/toolbar_bg"
|
||||
local:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
local:popupTheme="@style/ThemeOverlay.AppCompat.Light" >
|
||||
|
||||
|
||||
</android.support.v7.widget.Toolbar>
|
31
src/MaterialTest2/Resources/values-v21/styles.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<resources>
|
||||
<!--
|
||||
Base application theme for API 21+. This theme replaces
|
||||
MyTheme from resources/values/styles.xml on API 21+ devices.
|
||||
-->
|
||||
<style name="MyTheme" parent="MyTheme.Base">
|
||||
<item name="android:windowContentTransitions">true</item>
|
||||
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
||||
<item name="android:windowAllowReturnTransitionOverlap">true</item>
|
||||
<item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
|
||||
<item name="android:windowSharedElementExitTransition">@android:transition/move</item>
|
||||
<item name="android:actionModeBackground">@color/appAccentColor</item>
|
||||
</style>
|
||||
<style name="MyTheme_ActionBar" parent="MyTheme_ActionBar.Base">
|
||||
<item name="android:actionModeBackground">@color/appAccentColor</item>
|
||||
<item name="actionModeBackground">@color/appAccentColor</item>
|
||||
</style>
|
||||
|
||||
<style name="EntryFieldHeader">
|
||||
<item name="android:drawablePadding">2dp</item>
|
||||
<item name="android:layout_marginLeft">0dip</item>
|
||||
<item name="android:layout_marginRight">12dip</item>
|
||||
<item name="android:layout_marginBottom">3dp</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:paddingLeft">4dp</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
</style>
|
||||
</resources>
|
14
src/MaterialTest2/Resources/values/Strings.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="Hello">Hello World, Click Me!</string>
|
||||
<string name="ApplicationName">MaterialTest</string>
|
||||
<string name="app_name">MaterialTest</string>
|
||||
<string name="action_settings">Settings</string>
|
||||
<string name="drawer_open">open</string>
|
||||
<string name="drawer_close">close</string>
|
||||
<string name="group">Group</string>
|
||||
|
||||
<string name="action_search">Search</string>
|
||||
<string name="search_hint">Search Places</string>
|
||||
<string name="search_settings">Search Places</string>
|
||||
</resources>
|
5
src/MaterialTest2/Resources/values/dimens.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="detail_backdrop_height">200dp</dimen>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
</resources>
|
188
src/MaterialTest2/Resources/values/styles.xml
Normal file
@ -0,0 +1,188 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<resources>
|
||||
<style name="BottomBarButton" parEntryEditSingleLine_ImageView="@style/Widget.AppCompat.Button.Borderless">
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:layout_margin">6dp</item>
|
||||
<item name="android:layout_marginLeft">4dp</item>
|
||||
<item name="android:layout_marginRight">4dp</item>
|
||||
<item name="android:paddingBottom">10dp</item>
|
||||
<item name="android:paddingTop">10dp</item>
|
||||
<item name="android:paddingLeft">8dp</item>
|
||||
<item name="android:paddingRight">8dp</item>
|
||||
<item name="android:textColor">#2196f3</item>
|
||||
</style>
|
||||
<!--
|
||||
<style name="SplashScreenButton" parent="Widget.AppCompat.Button">
|
||||
<item name="colorButtonNormal">#fff</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:layout_height">32sp</item>
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:layout_marginBottom">8dp</item>
|
||||
</style>
|
||||
|
||||
-->
|
||||
|
||||
<style name="SplashScreenButton" parent="@style/Widget.AppCompat.Button" >
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:layout_height">40sp</item>
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:background">@drawable/splash_button_bg</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:layout_marginBottom">8dp</item>
|
||||
<item name="android:paddingLeft">16dp</item>
|
||||
<item name="android:drawablePadding">10dp</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
<item name="android:gravity">left|center_vertical</item>
|
||||
<item name="android:textColor">#737373</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="EntryEditSingleLine_ImageView">
|
||||
<item name="android:layout_width">40dp</item>
|
||||
<item name="android:layout_gravity">top</item>
|
||||
<item name="android:layout_marginRight">16dp</item>
|
||||
<item name="android:layout_marginTop">16dp</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_weight">0</item>
|
||||
</style>
|
||||
<style name="EntryEditSingleLine_TextInputLayout">
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
</style>
|
||||
<style name="EntryEditSingleLine_EditText">
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:paddingTop">0dp</item>
|
||||
</style>
|
||||
<style name="EntryEditSingleLine_container">
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:orientation">horizontal</item>
|
||||
<item name="android:gravity">center_vertical</item>
|
||||
</style>
|
||||
|
||||
<style name="MyTheme" parent="MyTheme.Base">
|
||||
<item name="actionModeBackground">@color/appAccentColor</item>
|
||||
</style>
|
||||
|
||||
|
||||
<!--style name="MyTheme.ActionMode" parent="@style/Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="actionModeBackground">#FFFFFF</item>
|
||||
</style-->
|
||||
|
||||
|
||||
<style name="MyTheme_ActionBar" parent="MyTheme_ActionBar.Base">
|
||||
<item name="actionModeBackground">@color/appAccentColor</item>
|
||||
</style>
|
||||
<!-- Base theme applied no matter what API -->
|
||||
<style name="MyTheme.Base" parent="Theme.AppCompat.Holo.Light.NoActionBar">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<!--We will be using the toolbar so no need to show ActionBar-->
|
||||
<item name="windowActionBar">false</item>
|
||||
<!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette-->
|
||||
<!-- colorPrimary is used for the default action bar background -->
|
||||
<item name="colorPrimary">@color/appPrimaryColor</item>
|
||||
<!-- colorPrimaryDark is used for the status bar -->
|
||||
<item name="colorPrimaryDark">@color/appPrimaryDarkColor</item>
|
||||
<!-- colorAccent is used as the default value for colorControlActivated
|
||||
which is used to tint widgets -->
|
||||
<item name="colorAccent">@color/appAccentColor</item>
|
||||
<!-- You can also set colorControlNormal, colorControlActivated
|
||||
colorControlHighlight and colorSwitchThumbNormal. -->
|
||||
<item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
|
||||
|
||||
</style>
|
||||
|
||||
<style name="MyTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<!--We will be using the toolbar so no need to show ActionBar-->
|
||||
<item name="windowActionBar">false</item>
|
||||
<!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette-->
|
||||
<!-- colorPrimary is used for the default action bar background -->
|
||||
<item name="colorPrimary">@color/appPrimaryColor</item>
|
||||
<!-- colorPrimaryDark is used for the status bar -->
|
||||
<item name="colorPrimaryDark">@color/appPrimaryDarkColor</item>
|
||||
<!-- colorAccent is used as the default value for colorControlActivated
|
||||
which is used to tint widgets -->
|
||||
<item name="colorAccent">@color/appAccentColor</item>
|
||||
<!-- You can also set colorControlNormal, colorControlActivated
|
||||
colorControlHighlight and colorSwitchThumbNormal. -->
|
||||
<item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
<style name="MyTheme_ActionBar.Base" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="windowActionBar">true</item>
|
||||
<item name="colorPrimary">@color/appPrimaryColor</item>
|
||||
<item name="colorPrimaryDark">@color/appPrimaryDarkColor</item>
|
||||
<item name="colorAccent">@color/appAccentColor</item>
|
||||
<item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
|
||||
|
||||
<item name="actionModeBackground">@color/appAccentColor</item>
|
||||
<item name="searchViewStyle">@style/CustomSearchViewStyle</item>
|
||||
<item name="autoCompleteTextViewStyle">@style/AutoCompleteTextView</item>
|
||||
|
||||
</style>
|
||||
<style name="AutoCompleteTextView" parent="Widget.AppCompat.Light.AutoCompleteTextView">
|
||||
<item name="android:textColor">#f0f</item>
|
||||
<item name="android:textCursorDrawable">@null</item>
|
||||
<item name="android:textColorHighlight">#0f0</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="CustomSearchViewStyle" parent="Widget.AppCompat.SearchView">
|
||||
<item name="queryBackground">@color/appPrimaryDarkColor</item>
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
|
||||
<item name="spinBars">true</item>
|
||||
<item name="color">@android:color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="GroupDetailInSearchResult">
|
||||
<item name="@android:textStyle">italic</item>
|
||||
</style>
|
||||
|
||||
<style name="Base_Dialog" parent="Theme.AppCompat.Light.Dialog" />
|
||||
<style name="Dialog" parent="Base_Dialog"></style>
|
||||
|
||||
|
||||
<style name="TextAppearance_EditEntry">
|
||||
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance_EditEntry_Value" parent="TextAppearance_EditEntry">
|
||||
<item name="android:gravity">center_vertical</item>
|
||||
<item name="android:layout_gravity">center_vertical</item>
|
||||
<item name="android:layout_marginRight">12dip</item>
|
||||
<item name="android:layout_marginLeft">12dip</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance_EditEntry_Small">
|
||||
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance_SmallHeading" parent="TextAppearance_EditEntry_Small">
|
||||
<item name="android:minHeight">24dip</item>
|
||||
<item name="android:layout_width">144dip</item>
|
||||
<item name="android:layout_marginTop">12dip</item>
|
||||
<item name="android:layout_marginRight">16dip</item>
|
||||
<item name="android:paddingRight">8dip</item>
|
||||
<item name="android:layout_marginBottom">-12dip</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
6
src/MaterialTest2/packages.config
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Xamarin.Android.Support.Design" version="22.2.1.0" targetFramework="MonoAndroid50" />
|
||||
<package id="Xamarin.Android.Support.v4" version="22.2.1.0" targetFramework="MonoAndroid50" />
|
||||
<package id="Xamarin.Android.Support.v7.AppCompat" version="22.2.1.0" targetFramework="MonoAndroid50" />
|
||||
</packages>
|
@ -10,8 +10,8 @@ namespace keepass2android
|
||||
{
|
||||
[Activity(Label = "@string/app_name",
|
||||
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
|
||||
Theme = "@style/NoTitleBar")]
|
||||
[IntentFilter(new[] { "kp2a.action.AboutActivity" }, Categories = new[] { Intent.CategoryDefault })]
|
||||
Theme = "@style/MyTheme_ActionBar")]
|
||||
[IntentFilter(new[] { "keepass2android.AboutActivity" }, Categories = new[] { Intent.CategoryDefault })]
|
||||
public class AboutActivity: Activity, IDialogInterfaceOnDismissListener
|
||||
{
|
||||
private AboutDialog _dialog;
|
||||
|
@ -5,6 +5,7 @@ using Android.Content.PM;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.OS;
|
||||
using Android.Preferences;
|
||||
using Android.Support.V7.App;
|
||||
using Android.Text;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
@ -19,8 +20,8 @@ namespace keepass2android
|
||||
{
|
||||
[Activity(Label = "@string/app_name",
|
||||
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
|
||||
Theme = "@style/Base")]
|
||||
public class CreateDatabaseActivity : Activity
|
||||
Theme = "@style/MyTheme_ActionBar")]
|
||||
public class CreateDatabaseActivity : AppCompatActivity
|
||||
{
|
||||
private IOConnectionInfo _ioc;
|
||||
private string _keyfileFilename;
|
||||
@ -57,6 +58,9 @@ namespace keepass2android
|
||||
base.OnCreate(bundle);
|
||||
_design.ApplyTheme();
|
||||
|
||||
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
SupportActionBar.SetHomeButtonEnabled(true);
|
||||
|
||||
SetContentView(Resource.Layout.create_database);
|
||||
_appTask = AppTask.GetTaskInOnCreate(bundle, Intent);
|
||||
|
||||
@ -140,7 +144,10 @@ namespace keepass2android
|
||||
_showPassword = !_showPassword;
|
||||
MakePasswordMaskedOrVisible();
|
||||
};
|
||||
|
||||
Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop;
|
||||
Android.Graphics.Color color = new Android.Graphics.Color (224, 224, 224);
|
||||
btnTogglePassword.SetColorFilter (color, mMode);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -579,6 +586,15 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override bool OnOptionsItemSelected(IMenuItem item)
|
||||
{
|
||||
switch (item.ItemId)
|
||||
{
|
||||
case Android.Resource.Id.Home:
|
||||
OnBackPressed();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ using KeePassLib.Utility;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Activity(Label = AppNames.AppName, Theme = "@style/Base")]
|
||||
[Activity(Label = AppNames.AppName, Theme = "@style/MyTheme_ActionBar")]
|
||||
public class DonateReminder : Activity
|
||||
{
|
||||
class Reminder
|
||||
@ -52,23 +52,23 @@ namespace keepass2android
|
||||
|
||||
yield return new Reminder
|
||||
{
|
||||
From = new DateTime(2016, 09, 17),
|
||||
From = new DateTime(2016, 09, 17),
|
||||
To = new DateTime(2016, 10, 04),
|
||||
Key = "DonationOktoberfest2016"
|
||||
,ResourceToShow = Resource.Layout.donate
|
||||
};
|
||||
yield return new Reminder
|
||||
{
|
||||
From = new DateTime(2015, 09, 16),
|
||||
To = new DateTime(2015, 10, 04),
|
||||
Key = "DonationOktoberfest2017"
|
||||
From = new DateTime(2017, 09, 16),
|
||||
To = new DateTime(2017, 10, 04),
|
||||
Key = "DonationOktoberfest2017b"//b because year was incorrectly set to 2015 in 0.9.8b
|
||||
,ResourceToShow = Resource.Layout.donate
|
||||
};
|
||||
yield return new Reminder
|
||||
{
|
||||
From = new DateTime(2015, 09, 22),
|
||||
To = new DateTime(2015, 10, 08),
|
||||
Key = "DonationOktoberfest2018"
|
||||
From = new DateTime(2018, 09, 22),
|
||||
To = new DateTime(2018, 10, 08),
|
||||
Key = "DonationOktoberfest2018b"//b because year was incorrectly set to 2015 in 0.9.8b
|
||||
,ResourceToShow = Resource.Layout.donate
|
||||
};
|
||||
|
||||
|
@ -44,7 +44,8 @@ using Uri = Android.Net.Uri;
|
||||
namespace keepass2android
|
||||
{
|
||||
|
||||
[Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden, Theme="@style/NoTitleBar")]
|
||||
[Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden,
|
||||
Theme = "@style/MyTheme_ActionBar")]
|
||||
public class EntryActivity : LockCloseActivity
|
||||
{
|
||||
public const String KeyEntry = "entry";
|
||||
@ -125,29 +126,7 @@ namespace keepass2android
|
||||
|
||||
}
|
||||
|
||||
protected void SetupMoveButtons() {
|
||||
View moveView = FindViewById(Resource.Id.entry_move);
|
||||
/* Disabled for simpler UI. Wait if users demand that button.
|
||||
if (App.Kp2a.GetDb().CanWrite)
|
||||
{
|
||||
moveView.Visibility = ViewStates.Visible;
|
||||
moveView.Click += (sender, e) =>
|
||||
{
|
||||
NavigateToFolderAndLaunchMoveElementTask navMoveTask =
|
||||
new NavigateToFolderAndLaunchMoveElementTask(Entry.ParentGroup,Entry.Uuid, false);
|
||||
navMoveTask.SetActivityResult(this, KeePass.ExitNormal );
|
||||
Finish();
|
||||
|
||||
};
|
||||
}
|
||||
else*/
|
||||
{
|
||||
moveView.Visibility = ViewStates.Gone;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class PluginActionReceiver : BroadcastReceiver
|
||||
{
|
||||
private readonly EntryActivity _activity;
|
||||
@ -328,9 +307,10 @@ namespace keepass2android
|
||||
|
||||
_showPassword =
|
||||
!prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default));
|
||||
|
||||
|
||||
RequestWindowFeature(WindowFeatures.IndeterminateProgress);
|
||||
base.OnCreate(savedInstanceState);
|
||||
RequestWindowFeature(WindowFeatures.IndeterminateProgress);
|
||||
|
||||
|
||||
new ActivityDesign(this).ApplyTheme();
|
||||
|
||||
@ -375,8 +355,7 @@ namespace keepass2android
|
||||
FillData();
|
||||
|
||||
SetupEditButtons();
|
||||
SetupMoveButtons ();
|
||||
|
||||
|
||||
App.Kp2a.GetDb().LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2a.GetDb().KpDatabase);
|
||||
|
||||
_pluginActionReceiver = new PluginActionReceiver(this);
|
||||
@ -455,12 +434,14 @@ namespace keepass2android
|
||||
private void PopulateExtraStrings()
|
||||
{
|
||||
ViewGroup extraGroup = (ViewGroup) FindViewById(Resource.Id.extra_strings);
|
||||
bool hasExtras = false;
|
||||
foreach (var pair in Entry.Strings.Where(pair => !PwDefs.IsStandardField(pair.Key)).OrderBy(pair => pair.Key))
|
||||
{
|
||||
hasExtras = true;
|
||||
var stringView = CreateExtraSection(pair.Key, pair.Value.ReadString(), pair.Value.IsProtected);
|
||||
extraGroup.AddView(stringView.View);
|
||||
}
|
||||
|
||||
FindViewById(Resource.Id.extra_strings_container).Visibility = hasExtras ? ViewStates.Visible : ViewStates.Gone;
|
||||
}
|
||||
|
||||
private ExtraStringView CreateExtraSection(string key, string value, bool isProtected)
|
||||
@ -676,7 +657,7 @@ namespace keepass2android
|
||||
if (extension != null)
|
||||
{
|
||||
MimeTypeMap mime = MimeTypeMap.Singleton;
|
||||
type = mime.GetMimeTypeFromExtension(extension);
|
||||
type = mime.GetMimeTypeFromExtension(extension.ToLowerInvariant());
|
||||
}
|
||||
return type;
|
||||
}
|
||||
@ -684,13 +665,13 @@ namespace keepass2android
|
||||
public override void OnBackPressed()
|
||||
{
|
||||
base.OnBackPressed();
|
||||
OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
||||
//OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
||||
}
|
||||
|
||||
protected void FillData()
|
||||
{
|
||||
_protectedTextViews = new List<TextView>();
|
||||
ImageView iv = (ImageView) FindViewById(Resource.Id.entry_icon);
|
||||
ImageView iv = (ImageView) FindViewById(Resource.Id.icon);
|
||||
if (iv != null)
|
||||
{
|
||||
iv.SetImageDrawable(Resources.GetDrawable(Resource.Drawable.ic00));
|
||||
@ -698,8 +679,9 @@ namespace keepass2android
|
||||
|
||||
|
||||
|
||||
ActionBar.Title = Entry.Strings.ReadSafe(PwDefs.TitleField);
|
||||
ActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
SupportActionBar.Title = Entry.Strings.ReadSafe(PwDefs.TitleField);
|
||||
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
SupportActionBar.SetHomeButtonEnabled(true);
|
||||
|
||||
PopulateGroupText (Resource.Id.entry_group_name, Resource.Id.entryfield_group_container, KeyGroupFullPath);
|
||||
|
||||
@ -736,7 +718,7 @@ namespace keepass2android
|
||||
}
|
||||
PopulateStandardText(Resource.Id.entry_comment, Resource.Id.entryfield_container_comment, PwDefs.NotesField);
|
||||
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.comment_container),
|
||||
FindViewById(Resource.Id.username_vdots), PwDefs.NotesField);
|
||||
FindViewById(Resource.Id.comment_vdots), PwDefs.NotesField);
|
||||
|
||||
PopulateText(Resource.Id.entry_tags, Resource.Id.entryfield_container_tags, concatTags(Entry.Tags));
|
||||
PopulateText(Resource.Id.entry_override_url, Resource.Id.entryfield_container_overrideurl, Entry.OverrideUrl);
|
||||
@ -912,6 +894,8 @@ namespace keepass2android
|
||||
return base.OnPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void UpdateTogglePasswordMenu()
|
||||
@ -1005,7 +989,7 @@ namespace keepass2android
|
||||
//So we can simply Finish. See this page for information on how to do this in more general (future?) cases:
|
||||
//http://developer.android.com/training/implementing-navigation/ancestral.html
|
||||
Finish();
|
||||
OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
||||
//OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace keepass2android
|
||||
get
|
||||
{
|
||||
//return new TextDrawable("\uF06E", _activity);
|
||||
return _activity.Resources.GetDrawable(Resource.Drawable.ic_action_eye_open);
|
||||
return _activity.Resources.GetDrawable(Resource.Drawable.ic_menu_view);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -33,12 +33,13 @@ using KeePassLib.Security;
|
||||
using Android.Content.PM;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
using Android.Util;
|
||||
using File = System.IO.File;
|
||||
using Uri = Android.Net.Uri;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden, Theme="@style/NoTitleBar")]
|
||||
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_ActionBar")]
|
||||
public class EntryEditActivity : LockCloseActivity {
|
||||
public const String KeyEntry = "entry";
|
||||
public const String KeyParent = "parent";
|
||||
@ -128,7 +129,7 @@ namespace keepass2android
|
||||
{
|
||||
String groupId = i.GetStringExtra(KeyParent);
|
||||
State.ParentGroup = db.KpDatabase.RootGroup.FindGroup(new PwUuid(MemUtil.HexStringToByteArray(groupId)), true);
|
||||
|
||||
|
||||
State.EntryInDatabase = new PwEntry(true, true);
|
||||
State.EntryInDatabase.Strings.Set(PwDefs.UserNameField, new ProtectedString(
|
||||
db.KpDatabase.MemoryProtection.ProtectUserName, db.KpDatabase.DefaultUserName));
|
||||
@ -206,7 +207,7 @@ namespace keepass2android
|
||||
if (State.SelectedIcon)
|
||||
{
|
||||
//TODO: custom image
|
||||
iconButton.SetImageResource(Icons.IconToResId(State.SelectedIconId));
|
||||
iconButton.SetImageResource(Icons.IconToResId(State.SelectedIconId, false));
|
||||
}
|
||||
iconButton.Click += (sender, evt) => {
|
||||
UpdateEntryFromUi(State.Entry);
|
||||
@ -215,8 +216,8 @@ namespace keepass2android
|
||||
|
||||
|
||||
// Generate password button
|
||||
Button generatePassword = (Button)FindViewById(Resource.Id.generate_button);
|
||||
generatePassword.Click += (sender, e) => {
|
||||
FindViewById(Resource.Id.generate_button).Click += (sender, e) =>
|
||||
{
|
||||
UpdateEntryFromUi(State.Entry);
|
||||
GeneratePasswordActivity.Launch(this);
|
||||
};
|
||||
@ -225,21 +226,20 @@ namespace keepass2android
|
||||
|
||||
|
||||
// Save button
|
||||
ActionBar.SetCustomView(Resource.Layout.SaveButton);
|
||||
ActionBar.SetDisplayShowCustomEnabled(true);
|
||||
ActionBar.SetDisplayShowTitleEnabled(false);
|
||||
ActionBar.SetDisplayUseLogoEnabled(false);
|
||||
ActionBar.SetDisplayShowHomeEnabled(false);
|
||||
ActionBar.SetDisplayOptions(ActionBarDisplayOptions.ShowCustom,
|
||||
ActionBarDisplayOptions.ShowCustom);
|
||||
var save = FindViewById(Resource.Id.entry_save);
|
||||
save.Click += (sender, e) =>
|
||||
{
|
||||
SaveEntry();
|
||||
};
|
||||
//SupportActionBar.SetCustomView(Resource.Layout.SaveButton);
|
||||
|
||||
if (State.IsNew)
|
||||
{
|
||||
SupportActionBar.Title = GetString(Resource.String.add_entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
SupportActionBar.Title = GetString(Resource.String.edit_entry);
|
||||
}
|
||||
|
||||
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
SupportActionBar.SetHomeButtonEnabled(true);
|
||||
|
||||
FindViewById(Resource.Id.entry_save_cancel).Click += (sender, args) => Finish();
|
||||
|
||||
// Respect mask password setting
|
||||
MakePasswordVisibleOrHidden();
|
||||
|
||||
@ -249,6 +249,9 @@ namespace keepass2android
|
||||
State.ShowPassword = !State.ShowPassword;
|
||||
MakePasswordVisibleOrHidden();
|
||||
};
|
||||
Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop;
|
||||
Android.Graphics.Color color = new Android.Graphics.Color (189,189,189);
|
||||
btnTogglePassword.SetColorFilter (color, mMode);
|
||||
|
||||
|
||||
Button addButton = (Button) FindViewById(Resource.Id.add_advanced);
|
||||
@ -258,7 +261,7 @@ namespace keepass2android
|
||||
LinearLayout container = (LinearLayout) FindViewById(Resource.Id.advanced_container);
|
||||
|
||||
KeyValuePair<string, ProtectedString> pair = new KeyValuePair<string, ProtectedString>("" , new ProtectedString(true, ""));
|
||||
LinearLayout ees = CreateExtraStringView(pair);
|
||||
View ees = CreateExtraStringView(pair);
|
||||
container.AddView(ees);
|
||||
|
||||
State.EntryModified = true;
|
||||
@ -669,13 +672,13 @@ namespace keepass2android
|
||||
//somehow after re-creating the activity. Maybe a Mono for Android bug?
|
||||
Intent intent = Intent;
|
||||
intent.PutExtra(IntentContinueWithEditing, true);
|
||||
OverridePendingTransition(0, 0);
|
||||
//OverridePendingTransition(0, 0);
|
||||
intent.AddFlags(ActivityFlags.NoAnimation | ActivityFlags.ForwardResult);
|
||||
_closeForReload = true;
|
||||
SetResult(KeePass.ExitRefreshTitle); //probably the entry will be modified -> let the EditActivity refresh to be safe
|
||||
Finish();
|
||||
|
||||
OverridePendingTransition(0, 0);
|
||||
//OverridePendingTransition(0, 0);
|
||||
StartActivity(intent);
|
||||
}
|
||||
|
||||
@ -745,9 +748,11 @@ namespace keepass2android
|
||||
{
|
||||
label = "<attachment>";
|
||||
}
|
||||
Button binaryButton = new Button(this) {Text = label};
|
||||
//Button binaryButton = new Button(this, null, Resource.Style.EditEntryButton) {Text = label};
|
||||
Button binaryButton = (Button)LayoutInflater.Inflate(Resource.Layout.EntryEditButtonDelete, null);
|
||||
binaryButton.Text = label;
|
||||
|
||||
binaryButton.SetCompoundDrawablesWithIntrinsicBounds( Resources.GetDrawable(Android.Resource.Drawable.IcMenuDelete),null, null, null);
|
||||
//binaryButton.SetCompoundDrawablesWithIntrinsicBounds( Resources.GetDrawable(Android.Resource.Drawable.IcMenuDelete),null, null, null);
|
||||
binaryButton.Click += (sender, e) =>
|
||||
{
|
||||
State.EntryModified = true;
|
||||
@ -759,10 +764,14 @@ namespace keepass2android
|
||||
|
||||
|
||||
}
|
||||
|
||||
//Button addBinaryButton = new Button(this, null, Resource.Style.EditEntryButton ) {Text = GetString(Resource.String.add_binary)};
|
||||
//addBinaryButton.SetCompoundDrawablesWithIntrinsicBounds( Resources.GetDrawable(Android.Resource.Drawable.IcMenuAdd) , null, null, null);
|
||||
Button addBinaryButton = (Button)LayoutInflater.Inflate(Resource.Layout.EntryEditButtonAdd, null);
|
||||
addBinaryButton.Text = GetString(Resource.String.add_binary);
|
||||
|
||||
Button addBinaryButton = new Button(this) {Text = GetString(Resource.String.add_binary)};
|
||||
addBinaryButton.SetCompoundDrawablesWithIntrinsicBounds( Resources.GetDrawable(Android.Resource.Drawable.IcMenuAdd) , null, null, null);
|
||||
addBinaryButton.Enabled = true;
|
||||
|
||||
if (!App.Kp2a.GetDb().DatabaseFormat.CanHaveMultipleAttachments)
|
||||
addBinaryButton.Enabled = !State.Entry.Binaries.Any();
|
||||
addBinaryButton.Click += (sender, e) =>
|
||||
@ -795,13 +804,18 @@ namespace keepass2android
|
||||
|
||||
public override bool OnOptionsItemSelected(IMenuItem item) {
|
||||
switch ( item.ItemId ) {
|
||||
case Resource.Id.menu_donate:
|
||||
return Util.GotoDonateUrl(this);
|
||||
case Resource.Id.menu_save:
|
||||
SaveEntry();
|
||||
return true;
|
||||
case Resource.Id.menu_cancel:
|
||||
Finish();
|
||||
return true;
|
||||
case Android.Resource.Id.Home:
|
||||
OnBackPressed();
|
||||
return true;
|
||||
default:
|
||||
return base.OnOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return base.OnOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
@ -819,15 +833,18 @@ namespace keepass2android
|
||||
FindViewById(Resource.Id.entry_expires).Enabled = State.Entry.Expires;
|
||||
}
|
||||
|
||||
public override Java.Lang.Object OnRetainNonConfigurationInstance()
|
||||
/*
|
||||
* TODO required??
|
||||
*
|
||||
* public override Java.Lang.Object OnRetainNonConfigurationInstance()
|
||||
{
|
||||
UpdateEntryFromUi(State.Entry);
|
||||
return this;
|
||||
}
|
||||
}*/
|
||||
|
||||
LinearLayout CreateExtraStringView(KeyValuePair<string, ProtectedString> pair)
|
||||
RelativeLayout CreateExtraStringView(KeyValuePair<string, ProtectedString> pair)
|
||||
{
|
||||
LinearLayout ees = (LinearLayout)LayoutInflater.Inflate(Resource.Layout.entry_edit_section, null);
|
||||
RelativeLayout ees = (RelativeLayout)LayoutInflater.Inflate(Resource.Layout.entry_edit_section, null);
|
||||
((TextView)ees.FindViewById(Resource.Id.title)).Text = pair.Key;
|
||||
((TextView)ees.FindViewById(Resource.Id.title)).TextChanged += (sender, e) => State.EntryModified = true;
|
||||
((TextView)ees.FindViewById(Resource.Id.value)).Text = pair.Value.ReadString();
|
||||
@ -896,7 +913,7 @@ namespace keepass2android
|
||||
|
||||
private void FillData() {
|
||||
ImageButton currIconButton = (ImageButton) FindViewById(Resource.Id.icon_button);
|
||||
App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(currIconButton, Resources, App.Kp2a.GetDb().KpDatabase, State.Entry.IconId, State.Entry.CustomIconUuid);
|
||||
App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(currIconButton, Resources, App.Kp2a.GetDb().KpDatabase, State.Entry.IconId, State.Entry.CustomIconUuid, false);
|
||||
|
||||
PopulateText(Resource.Id.entry_title, State.Entry.Strings.ReadSafe (PwDefs.TitleField));
|
||||
PopulateText(Resource.Id.entry_user_name, State.Entry.Strings.ReadSafe (PwDefs.UserNameField));
|
||||
@ -928,8 +945,7 @@ namespace keepass2android
|
||||
}
|
||||
else
|
||||
{
|
||||
FindViewById(Resource.Id.entry_override_url_label).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.entry_override_url).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.entry_override_url_container).Visibility = ViewStates.Gone;
|
||||
}
|
||||
|
||||
if (App.Kp2a.GetDb().DatabaseFormat.SupportsTags)
|
||||
@ -938,7 +954,9 @@ namespace keepass2android
|
||||
}
|
||||
else
|
||||
{
|
||||
FindViewById(Resource.Id.entry_tags_label).Visibility = ViewStates.Gone;
|
||||
var view = FindViewById(Resource.Id.entry_tags_label);
|
||||
if (view != null)
|
||||
view.Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.entry_tags).Visibility = ViewStates.Gone;
|
||||
}
|
||||
|
||||
@ -1016,6 +1034,11 @@ namespace keepass2android
|
||||
|
||||
private void PopulateText(int viewId, String text) {
|
||||
TextView tv = (TextView) FindViewById(viewId);
|
||||
if (tv == null)
|
||||
{
|
||||
Kp2aLog.Log("Invalid viewId " + viewId);
|
||||
return;
|
||||
}
|
||||
tv.Text = text;
|
||||
tv.TextChanged += (sender, e) => {State.EntryModified = true;};
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ namespace keepass2android
|
||||
|
||||
[Activity(Label = "@string/app_name",
|
||||
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
|
||||
Theme = "@style/Base")]
|
||||
Theme = "@style/MyTheme_ActionBar")]
|
||||
[IntentFilter(new[] {"kp2a.action.ExportDatabaseActivity"}, Categories = new[] {Intent.CategoryDefault})]
|
||||
public class ExportDatabaseActivity : LockCloseActivity
|
||||
{
|
||||
@ -62,7 +62,7 @@ namespace keepass2android
|
||||
}
|
||||
else
|
||||
{
|
||||
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this,
|
||||
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this,
|
||||
OnActivityResult,
|
||||
defaultPath =>
|
||||
{
|
||||
|
@ -1,18 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.Content.Res;
|
||||
using Android.Graphics;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.OS;
|
||||
using Android.Support.V4.Content;
|
||||
using Android.Support.V7.App;
|
||||
using Android.Text;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using keepass2android.Io;
|
||||
using keepass2android.view;
|
||||
using AlertDialog = Android.App.AlertDialog;
|
||||
using Object = Java.Lang.Object;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden , Theme="@style/NoTitleBar")]
|
||||
public class FileStorageSelectionActivity : ListActivity
|
||||
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_Blue")]
|
||||
public class FileStorageSelectionActivity : AppCompatActivity
|
||||
{
|
||||
private readonly ActivityDesign _design;
|
||||
|
||||
@ -78,8 +87,59 @@ namespace keepass2android
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static float convertDpToPixel(float dp, Context context)
|
||||
{
|
||||
Resources resources = context.Resources;
|
||||
DisplayMetrics metrics = resources.DisplayMetrics;
|
||||
float px = dp * metrics.Density;
|
||||
return px;
|
||||
}
|
||||
|
||||
|
||||
public override View GetView(int position, View convertView, ViewGroup parent)
|
||||
{
|
||||
|
||||
Button btn;
|
||||
|
||||
if (convertView == null)
|
||||
{ // if it's not recycled, initialize some attributes
|
||||
|
||||
btn = new Button(_context);
|
||||
btn.LayoutParameters = new GridView.LayoutParams((int)convertDpToPixel(90, _context), (int)convertDpToPixel(110, _context));
|
||||
btn.SetBackgroundResource(Resource.Drawable.storagetype_button_bg);
|
||||
btn.SetPadding((int)convertDpToPixel(4, _context),
|
||||
(int)convertDpToPixel(20, _context),
|
||||
(int)convertDpToPixel(4, _context),
|
||||
(int)convertDpToPixel(4, _context));
|
||||
btn.SetTextSize(ComplexUnitType.Sp, 11);
|
||||
btn.SetTextColor(new Color(115, 115, 115));
|
||||
btn.SetSingleLine(false);
|
||||
btn.Gravity = GravityFlags.Center;
|
||||
btn.Click += (sender, args) => _context.OnItemSelected( (string) ((Button)sender).Tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
btn = (Button)convertView;
|
||||
}
|
||||
|
||||
var protocolId = _protocolIds[position];
|
||||
btn.Tag = protocolId;
|
||||
Drawable drawable = App.Kp2a.GetResourceDrawable("ic_storage_" + protocolId);
|
||||
|
||||
String title = App.Kp2a.GetResourceString("filestoragename_" + protocolId);
|
||||
var str = new SpannableString(title);
|
||||
|
||||
btn.TextFormatted = str;
|
||||
//var drawable = ContextCompat.GetDrawable(context, Resource.Drawable.Icon);
|
||||
btn.SetCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null);
|
||||
|
||||
return btn;
|
||||
#if NoNet
|
||||
TODO: kp2a button
|
||||
#endif
|
||||
/*
|
||||
if (_protocolIds[position] == "kp2a")
|
||||
{
|
||||
return new FileStorageViewKp2a(_context);
|
||||
@ -89,7 +149,7 @@ namespace keepass2android
|
||||
var view = new FileStorageView(_context, _protocolIds[position], position);
|
||||
return view;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
@ -145,16 +205,32 @@ namespace keepass2android
|
||||
|
||||
SetContentView(Resource.Layout.filestorage_selection);
|
||||
|
||||
_fileStorageAdapter = new FileStorageAdapter(this);
|
||||
ListAdapter = _fileStorageAdapter;
|
||||
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar);
|
||||
|
||||
SetSupportActionBar(toolbar);
|
||||
|
||||
ListView listView = FindViewById<ListView>(Android.Resource.Id.List);
|
||||
listView.ItemClick +=
|
||||
SupportActionBar.Title = RemoveTrailingColon(GetString(Resource.String.select_storage_type));
|
||||
|
||||
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
SupportActionBar.SetDisplayShowHomeEnabled(true);
|
||||
toolbar.NavigationClick += (sender, args) => OnBackPressed();
|
||||
|
||||
_fileStorageAdapter = new FileStorageAdapter(this);
|
||||
var gridView = FindViewById<GridView>(Resource.Id.gridview);
|
||||
gridView.ItemClick +=
|
||||
(sender, args) => OnItemSelected((string)_fileStorageAdapter.GetItem(args.Position));
|
||||
//listView.ItemsCanFocus = true;
|
||||
gridView.Adapter = _fileStorageAdapter;
|
||||
|
||||
}
|
||||
|
||||
protected override void OnResume()
|
||||
private string RemoveTrailingColon(string str)
|
||||
{
|
||||
if (str.EndsWith(":"))
|
||||
return str.Substring(0, str.Length - 1);
|
||||
return str;
|
||||
}
|
||||
|
||||
protected override void OnResume()
|
||||
{
|
||||
base.OnResume();
|
||||
_design.ReapplyTheme();
|
||||
|
88
src/keepass2android/FixedDrawerLayout.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Util;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
public class FixedDrawerLayout : Android.Support.V4.Widget.DrawerLayout
|
||||
{
|
||||
private bool _fitsSystemWindows;
|
||||
|
||||
protected FixedDrawerLayout(IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
|
||||
public FixedDrawerLayout(Context context, IAttributeSet attrs, int defStyle)
|
||||
: base(context, attrs, defStyle)
|
||||
{
|
||||
}
|
||||
|
||||
public FixedDrawerLayout(Context context, IAttributeSet attrs)
|
||||
: base(context, attrs)
|
||||
{
|
||||
}
|
||||
|
||||
public FixedDrawerLayout(Context context)
|
||||
: base(context)
|
||||
{
|
||||
}
|
||||
|
||||
private int[] mInsets = new int[4];
|
||||
|
||||
protected override bool FitSystemWindows(Rect insets)
|
||||
{
|
||||
if (Build.VERSION.SdkInt >= Build.VERSION_CODES.Kitkat)
|
||||
{
|
||||
// Intentionally do not modify the bottom inset. For some reason,
|
||||
// if the bottom inset is modified, window resizing stops working.
|
||||
// TODO: Figure out why.
|
||||
|
||||
mInsets[0] = insets.Left;
|
||||
mInsets[1] = insets.Top;
|
||||
mInsets[2] = insets.Right;
|
||||
|
||||
insets.Left = 0;
|
||||
insets.Top = 0;
|
||||
insets.Right = 0;
|
||||
}
|
||||
|
||||
return base.FitSystemWindows(insets);
|
||||
|
||||
}
|
||||
public int[] GetInsets()
|
||||
{
|
||||
return mInsets;
|
||||
}
|
||||
|
||||
public struct MeasureArgs
|
||||
{
|
||||
public int ActualHeight;
|
||||
public int ProposedHeight;
|
||||
|
||||
}
|
||||
|
||||
public event EventHandler<MeasureArgs> MeasureEvent;
|
||||
|
||||
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
MeasureArgs args;
|
||||
|
||||
args.ProposedHeight = MeasureSpec.GetSize(heightMeasureSpec);
|
||||
args.ActualHeight = Height;
|
||||
|
||||
|
||||
OnMeasureEvent(args);
|
||||
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
protected virtual void OnMeasureEvent(MeasureArgs args)
|
||||
{
|
||||
var handler = MeasureEvent;
|
||||
if (handler != null) handler(this, args);
|
||||
}
|
||||
}
|
||||
}
|
@ -26,8 +26,8 @@ using Android.Widget;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Activity (Label = "@string/app_name", Theme="@style/NoTitleBar")]
|
||||
public class GeneratePasswordActivity : LockCloseActivity {
|
||||
[Activity(Label = "@string/app_name", Theme = "@style/MyTheme_ActionBar", WindowSoftInputMode = SoftInput.StateHidden)]
|
||||
public class GeneratePasswordActivity : LockCloseActivity {
|
||||
private readonly int[] _buttonIds = new[] {Resource.Id.btn_length6, Resource.Id.btn_length8, Resource.Id.btn_length12, Resource.Id.btn_length16};
|
||||
|
||||
public static void Launch(Activity act) {
|
||||
@ -78,7 +78,7 @@ namespace keepass2android
|
||||
genPassButton.Click += (sender, e) => {
|
||||
String password = GeneratePassword();
|
||||
|
||||
EditText txtPassword = (EditText) FindViewById(Resource.Id.password);
|
||||
EditText txtPassword = (EditText) FindViewById(Resource.Id.password_edit);
|
||||
txtPassword.Text = password;
|
||||
};
|
||||
|
||||
@ -86,7 +86,7 @@ namespace keepass2android
|
||||
|
||||
View acceptButton = FindViewById(Resource.Id.accept_button);
|
||||
acceptButton.Click += (sender, e) => {
|
||||
EditText password = (EditText) FindViewById(Resource.Id.password);
|
||||
EditText password = (EditText) FindViewById(Resource.Id.password_edit);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.PutExtra("keepass2android.password.generated_password", password.Text);
|
||||
@ -106,9 +106,12 @@ namespace keepass2android
|
||||
};
|
||||
|
||||
|
||||
EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password);
|
||||
EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit);
|
||||
txtPasswordToSet.Text = GeneratePassword();
|
||||
|
||||
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
SupportActionBar.SetHomeButtonEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
public String GeneratePassword() {
|
||||
@ -157,6 +160,18 @@ namespace keepass2android
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
|
||||
public override bool OnOptionsItemSelected(IMenuItem item)
|
||||
{
|
||||
switch (item.ItemId)
|
||||
{
|
||||
case Android.Resource.Id.Home:
|
||||
OnBackPressed();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,11 +26,24 @@ using Android.Util;
|
||||
using KeePassLib.Utility;
|
||||
using keepass2android.view;
|
||||
using Android.Content.PM;
|
||||
using Android.Runtime;
|
||||
using Android.Support.V4.View;
|
||||
using Android.Support.V7.App;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden , Theme="@style/NoTitleBar")]
|
||||
[MetaData("android.app.default_searchable",Value="keepass2android.search.SearchResults")]
|
||||
|
||||
|
||||
//[IntentFilter(new[] { "android.intent.action.SEARCH" })]
|
||||
//[MetaData("android.app.default_searchable", Value = "MaterialTest2.EntryEditActivity")]
|
||||
//[MetaData("android.app.searchable", Resource = "@xml/searchable_mattest")]
|
||||
|
||||
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_ActionBar")]
|
||||
//[MetaData("android.app.searchable", Resource = "@xml/searchable_mattest")]
|
||||
//[MetaData("android.app.default_searchable",Value="keepass2android.search.SearchResults")]
|
||||
[IntentFilter(new[] { "android.intent.action.SEARCH" })]
|
||||
[MetaData("android.app.default_searchable", Value = "MaterialTest2.EntryEditActivity")]
|
||||
[MetaData("android.app.searchable", Resource = "@xml/searchable_mattest")]
|
||||
[IntentFilter(new string[]{"android.intent.action.SEARCH"})]
|
||||
[MetaData("android.app.searchable",Resource=AppNames.Searchable)]
|
||||
public class GroupActivity : GroupBaseActivity {
|
||||
@ -77,21 +90,15 @@ namespace keepass2android
|
||||
|
||||
public override void SetupNormalButtons()
|
||||
{
|
||||
GroupView.SetNormalButtonVisibility(AddGroupEnabled, AddEntryEnabled);
|
||||
GroupView.Invalidate();
|
||||
SetNormalButtonVisibility(AddGroupEnabled, AddEntryEnabled);
|
||||
}
|
||||
|
||||
private bool AddGroupEnabled
|
||||
{
|
||||
get { return App.Kp2a.GetDb().CanWrite; }
|
||||
}
|
||||
private bool AddEntryEnabled
|
||||
protected override bool AddEntryEnabled
|
||||
{
|
||||
get { return App.Kp2a.GetDb().CanWrite && ((this.Group.ParentGroup != null) || App.Kp2a.GetDb().DatabaseFormat.CanHaveEntriesInRootGroup); }
|
||||
}
|
||||
|
||||
|
||||
protected override void OnCreate (Bundle savedInstanceState)
|
||||
|
||||
protected override void OnCreate (Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreate (savedInstanceState);
|
||||
|
||||
@ -123,26 +130,24 @@ namespace keepass2android
|
||||
|
||||
if (AddGroupEnabled) {
|
||||
// Add Group button
|
||||
View addGroup = FindViewById (Resource.Id.add_group);
|
||||
View addGroup = FindViewById (Resource.Id.fabAddNewGroup);
|
||||
addGroup.Click += (sender, e) => {
|
||||
GroupEditActivity.Launch (this, Group);
|
||||
};
|
||||
}
|
||||
|
||||
if (AddEntryEnabled) {
|
||||
// Add Entry button
|
||||
View addEntry = FindViewById (Resource.Id.add_entry);
|
||||
addEntry.Click += (sender, e) => {
|
||||
EntryEditActivity.Launch (this, Group, AppTask);
|
||||
|
||||
};
|
||||
|
||||
if (AddEntryEnabled)
|
||||
{
|
||||
View addEntry = FindViewById (Resource.Id.fabAddNewEntry);
|
||||
addEntry.Click += (sender, e) => { EntryEditActivity.Launch (this, Group, AppTask); };
|
||||
|
||||
}
|
||||
|
||||
SetGroupTitle();
|
||||
SetGroupIcon();
|
||||
|
||||
ListAdapter = new PwGroupListAdapter(this, Group);
|
||||
RegisterForContextMenu(ListView);
|
||||
FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group);
|
||||
Log.Warn(Tag, "Finished creating group");
|
||||
|
||||
}
|
||||
@ -158,8 +163,8 @@ namespace keepass2android
|
||||
public override void OnBackPressed()
|
||||
{
|
||||
base.OnBackPressed();
|
||||
if ((Group != null) && (Group.ParentGroup != null))
|
||||
OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
||||
//if ((Group != null) && (Group.ParentGroup != null))
|
||||
//OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
||||
}
|
||||
|
||||
public override bool OnContextItemSelected(IMenuItem item) {
|
||||
|
@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
@ -33,11 +34,14 @@ using keepass2android.Io;
|
||||
using keepass2android.database.edit;
|
||||
using keepass2android.view;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.Support.V4.View;
|
||||
using CursorAdapter = Android.Support.V4.Widget.CursorAdapter;
|
||||
using Object = Java.Lang.Object;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
|
||||
public abstract class GroupBaseActivity : LockCloseListActivity {
|
||||
public abstract class GroupBaseActivity : LockCloseActivity {
|
||||
public const String KeyEntry = "entry";
|
||||
public const String KeyMode = "mode";
|
||||
|
||||
@ -65,9 +69,47 @@ namespace keepass2android
|
||||
|
||||
public virtual void SetupNormalButtons()
|
||||
{
|
||||
GroupView.SetNormalButtonVisibility(App.Kp2a.GetDb().CanWrite, App.Kp2a.GetDb().CanWrite);
|
||||
SetNormalButtonVisibility(AddGroupEnabled, AddEntryEnabled);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected virtual bool AddGroupEnabled
|
||||
{
|
||||
get { return App.Kp2a.GetDb().CanWrite; }
|
||||
}
|
||||
protected virtual bool AddEntryEnabled
|
||||
{
|
||||
get { return App.Kp2a.GetDb().CanWrite; }
|
||||
}
|
||||
|
||||
public void SetNormalButtonVisibility(bool showAddGroup, bool showAddEntry)
|
||||
{
|
||||
//check for null in the following because the "empty" layouts may not have all views
|
||||
|
||||
if (FindViewById(Resource.Id.bottom_bar) != null)
|
||||
FindViewById(Resource.Id.bottom_bar).Visibility = BottomBarAlwaysVisible ? ViewStates.Visible : ViewStates.Gone;
|
||||
|
||||
if (FindViewById(Resource.Id.divider2) != null)
|
||||
FindViewById(Resource.Id.divider2).Visibility = BottomBarAlwaysVisible ? ViewStates.Visible : ViewStates.Gone;
|
||||
|
||||
if (FindViewById(Resource.Id.fabCancelAddNew) != null)
|
||||
{
|
||||
FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fabAddNewGroup).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fabAddNewEntry).Visibility = ViewStates.Gone;
|
||||
|
||||
FindViewById(Resource.Id.fabAddNew).Visibility = (showAddGroup || showAddEntry) ? ViewStates.Visible : ViewStates.Gone;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public virtual bool BottomBarAlwaysVisible
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
|
||||
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||
{
|
||||
base.OnActivityResult(requestCode, resultCode, data);
|
||||
@ -122,10 +164,12 @@ namespace keepass2android
|
||||
protected PwGroup Group;
|
||||
|
||||
internal AppTask AppTask;
|
||||
protected GroupView GroupView;
|
||||
|
||||
|
||||
private String strCachedGroupUuid = null;
|
||||
public String UuidGroup {
|
||||
|
||||
|
||||
|
||||
public String UuidGroup {
|
||||
get {
|
||||
if (strCachedGroupUuid == null) {
|
||||
strCachedGroupUuid = MemUtil.ByteArrayToHexString (Group.Uuid.UuidBytes);
|
||||
@ -155,24 +199,26 @@ namespace keepass2android
|
||||
Database db = App.Kp2a.GetDb();
|
||||
if ( db.Dirty.Contains(Group) ) {
|
||||
db.Dirty.Remove(Group);
|
||||
BaseAdapter adapter = (BaseAdapter) ListAdapter;
|
||||
adapter.NotifyDataSetChanged();
|
||||
ListAdapter.NotifyDataSetChanged();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnListItemClick(ListView l, View v, int position, long id) {
|
||||
base.OnListItemClick(l, v, position, id);
|
||||
|
||||
IListAdapter adapt = ListAdapter;
|
||||
ClickView cv = (ClickView) adapt.GetView(position, null, null);
|
||||
cv.OnClick();
|
||||
|
||||
}
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState) {
|
||||
|
||||
public BaseAdapter ListAdapter
|
||||
{
|
||||
get { return (BaseAdapter) FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter; }
|
||||
}
|
||||
|
||||
public virtual bool IsSearchResult
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState) {
|
||||
base.OnCreate(savedInstanceState);
|
||||
|
||||
|
||||
Android.Util.Log.Debug("KP2A", "Creating GBA");
|
||||
|
||||
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
|
||||
|
||||
// Likely the app has been killed exit the activity
|
||||
@ -183,36 +229,63 @@ namespace keepass2android
|
||||
|
||||
_prefs = PreferenceManager.GetDefaultSharedPreferences(this);
|
||||
|
||||
GroupView = new GroupView(this);
|
||||
SetContentView(GroupView);
|
||||
|
||||
FindViewById(Resource.Id.cancel_insert_element).Click += (sender, args) => StopMovingElement();
|
||||
FindViewById(Resource.Id.insert_element).Click += (sender, args) => InsertElement();
|
||||
|
||||
SetResult(KeePass.ExitNormal);
|
||||
|
||||
StyleScrollBars();
|
||||
SetContentView(ContentResourceId);
|
||||
|
||||
if (FindViewById(Resource.Id.fabCancelAddNew) != null)
|
||||
{
|
||||
FindViewById(Resource.Id.fabAddNew).Click += (sender, args) =>
|
||||
{
|
||||
FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.fabAddNewGroup).Visibility = AddGroupEnabled ? ViewStates.Visible : ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fabAddNewEntry).Visibility = AddEntryEnabled ? ViewStates.Visible : ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fabAddNew).Visibility = ViewStates.Gone;
|
||||
};
|
||||
|
||||
FindViewById(Resource.Id.fabCancelAddNew).Click += (sender, args) =>
|
||||
{
|
||||
FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fabAddNewGroup).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fabAddNewEntry).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fabAddNew).Visibility = ViewStates.Visible;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (FindViewById(Resource.Id.cancel_insert_element) != null)
|
||||
{
|
||||
FindViewById(Resource.Id.cancel_insert_element).Click += (sender, args) => StopMovingElements();
|
||||
FindViewById(Resource.Id.insert_element).Click += (sender, args) => InsertElements();
|
||||
}
|
||||
|
||||
|
||||
SetResult(KeePass.ExitNormal);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void InsertElement()
|
||||
protected virtual int ContentResourceId
|
||||
{
|
||||
MoveElementTask moveElementTask = (MoveElementTask)AppTask;
|
||||
IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(moveElementTask.Uuid, true, null);
|
||||
get { return Resource.Layout.group; }
|
||||
}
|
||||
|
||||
private void InsertElements()
|
||||
{
|
||||
MoveElementsTask moveElementsTask = (MoveElementsTask)AppTask;
|
||||
IEnumerable<IStructureItem> elementsToMove =
|
||||
moveElementsTask.Uuids.Select(uuid => App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(uuid, true, null));
|
||||
|
||||
|
||||
|
||||
var moveElement = new MoveElement(elementToMove, Group, this, App.Kp2a, new ActionOnFinish((success, message) => { StopMovingElement(); if (!String.IsNullOrEmpty(message)) Toast.MakeText(this, message, ToastLength.Long).Show();}));
|
||||
var moveElement = new MoveElements(elementsToMove.ToList(), Group, this, App.Kp2a, new ActionOnFinish((success, message) => { StopMovingElements(); if (!String.IsNullOrEmpty(message)) Toast.MakeText(this, message, ToastLength.Long).Show();}));
|
||||
var progressTask = new ProgressTask(App.Kp2a, this, moveElement);
|
||||
progressTask.Run();
|
||||
|
||||
}
|
||||
|
||||
protected void StyleScrollBars() {
|
||||
ListView lv = ListView;
|
||||
lv.ScrollBarStyle =ScrollbarStyles.InsideInset;
|
||||
lv.TextFilterEnabled = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected void SetGroupTitle()
|
||||
@ -228,31 +301,33 @@ namespace keepass2android
|
||||
titleText = GetText(Resource.String.root);
|
||||
}
|
||||
|
||||
ActionBar.Title = titleText;
|
||||
if (clickable)
|
||||
ActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
SupportActionBar.Title = titleText;
|
||||
if (clickable)
|
||||
{
|
||||
SupportActionBar.SetHomeButtonEnabled(true);
|
||||
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
SupportActionBar.SetDisplayShowHomeEnabled(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected void SetGroupIcon() {
|
||||
if (Group != null) {
|
||||
Drawable drawable = App.Kp2a.GetDb().DrawableFactory.GetIconDrawable(Resources, App.Kp2a.GetDb().KpDatabase, Group.IconId, Group.CustomIconUuid);
|
||||
ImageView iv = (ImageView) FindViewById(Resource.Id.icon);
|
||||
if (iv != null)
|
||||
iv.SetImageDrawable(drawable);
|
||||
if (Util.HasActionBar(this))
|
||||
ActionBar.SetIcon(drawable);
|
||||
Drawable drawable = App.Kp2a.GetDb().DrawableFactory.GetIconDrawable(Resources, App.Kp2a.GetDb().KpDatabase, Group.IconId, Group.CustomIconUuid, true);
|
||||
SupportActionBar.SetDisplayShowHomeEnabled(true);
|
||||
//SupportActionBar.SetIcon(drawable);
|
||||
}
|
||||
}
|
||||
|
||||
class SuggestionListener: Java.Lang.Object, SearchView.IOnSuggestionListener
|
||||
class SuggestionListener: Java.Lang.Object, SearchView.IOnSuggestionListener, Android.Support.V7.Widget.SearchView.IOnSuggestionListener
|
||||
{
|
||||
private readonly CursorAdapter _suggestionsAdapter;
|
||||
private readonly GroupBaseActivity _activity;
|
||||
private readonly IMenuItem _searchItem;
|
||||
|
||||
|
||||
public SuggestionListener(CursorAdapter suggestionsAdapter, GroupBaseActivity activity, IMenuItem searchItem)
|
||||
public SuggestionListener(Android.Support.V4.Widget.CursorAdapter suggestionsAdapter, GroupBaseActivity activity, IMenuItem searchItem)
|
||||
{
|
||||
_suggestionsAdapter = suggestionsAdapter;
|
||||
_activity = activity;
|
||||
@ -265,7 +340,6 @@ namespace keepass2android
|
||||
cursor.MoveToPosition(position);
|
||||
string entryIdAsHexString = cursor.GetString(cursor.GetColumnIndexOrThrow(SearchManager.SuggestColumnIntentDataId));
|
||||
EntryActivity.Launch(_activity, App.Kp2a.GetDb().Entries[new PwUuid(MemUtil.HexStringToByteArray(entryIdAsHexString))],-1,_activity.AppTask);
|
||||
((SearchView) _searchItem.ActionView).Iconified = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -275,7 +349,7 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
class OnQueryTextListener: Java.Lang.Object, SearchView.IOnQueryTextListener
|
||||
class OnQueryTextListener: Java.Lang.Object, Android.Support.V7.Widget.SearchView.IOnQueryTextListener
|
||||
{
|
||||
private readonly GroupBaseActivity _activity;
|
||||
|
||||
@ -305,35 +379,37 @@ namespace keepass2android
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnCreateOptionsMenu(IMenu menu) {
|
||||
base.OnCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = MenuInflater;
|
||||
inflater.Inflate(Resource.Menu.group, menu);
|
||||
if (Util.HasActionBar(this))
|
||||
{
|
||||
|
||||
public override bool OnCreateOptionsMenu(IMenu menu) {
|
||||
|
||||
MenuInflater inflater = MenuInflater;
|
||||
inflater.Inflate(Resource.Menu.group, menu);
|
||||
var searchManager = (SearchManager)GetSystemService (Context.SearchService);
|
||||
IMenuItem searchItem = menu.FindItem(Resource.Id.menu_search);
|
||||
var searchView = (SearchView) searchItem.ActionView;
|
||||
|
||||
var view = MenuItemCompat.GetActionView(searchItem);
|
||||
var searchView = view.JavaCast<Android.Support.V7.Widget.SearchView>();
|
||||
|
||||
searchView.SetSearchableInfo (searchManager.GetSearchableInfo (ComponentName));
|
||||
searchView.SetOnSuggestionListener(new SuggestionListener(searchView.SuggestionsAdapter, this, searchItem));
|
||||
searchView.SetOnQueryTextListener(new OnQueryTextListener(this));
|
||||
|
||||
|
||||
var item = menu.FindItem(Resource.Id.menu_sync);
|
||||
if (item != null)
|
||||
{
|
||||
if (App.Kp2a.GetDb().Ioc.IsLocalFile())
|
||||
item.SetVisible(false);
|
||||
else
|
||||
item.SetVisible(true);
|
||||
}
|
||||
|
||||
|
||||
return base.OnCreateOptionsMenu(menu);
|
||||
|
||||
}
|
||||
var item = menu.FindItem(Resource.Id.menu_sync);
|
||||
if (item != null)
|
||||
{
|
||||
if (App.Kp2a.GetDb().Ioc.IsLocalFile())
|
||||
item.SetVisible(false);
|
||||
else
|
||||
item.SetVisible(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public override bool OnPrepareOptionsMenu(IMenu menu) {
|
||||
if ( ! base.OnPrepareOptionsMenu(menu) ) {
|
||||
@ -376,7 +452,7 @@ namespace keepass2android
|
||||
//http://developer.android.com/training/implementing-navigation/ancestral.html
|
||||
AppTask.SetActivityResult(this, KeePass.ExitNormal);
|
||||
Finish();
|
||||
OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
||||
//OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -425,11 +501,11 @@ namespace keepass2android
|
||||
if (!String.IsNullOrEmpty(message))
|
||||
Toast.MakeText(this, message, ToastLength.Long).Show();
|
||||
|
||||
// Tell the adapter to refresh it's list
|
||||
// Tell the adapter to refresh it's list
|
||||
BaseAdapter adapter = (BaseAdapter)ListAdapter;
|
||||
adapter.NotifyDataSetChanged();
|
||||
|
||||
if (App.Kp2a.GetDb().OtpAuxFileIoc != null)
|
||||
|
||||
if (App.Kp2a.GetDb().OtpAuxFileIoc != null)
|
||||
{
|
||||
var task2 = new SyncOtpAuxFile(App.Kp2a.GetDb().OtpAuxFileIoc);
|
||||
new ProgressTask(App.Kp2a, this, task2).Run();
|
||||
@ -486,6 +562,7 @@ namespace keepass2android
|
||||
db.Dirty.Remove(Group);
|
||||
|
||||
// Tell the adapter to refresh it's list
|
||||
|
||||
BaseAdapter adapter = (BaseAdapter)ListAdapter;
|
||||
adapter.NotifyDataSetChanged();
|
||||
|
||||
@ -537,10 +614,10 @@ namespace keepass2android
|
||||
|
||||
public bool IsBeingMoved(PwUuid uuid)
|
||||
{
|
||||
MoveElementTask moveElementTask = AppTask as MoveElementTask;
|
||||
if (moveElementTask != null)
|
||||
MoveElementsTask moveElementsTask = AppTask as MoveElementsTask;
|
||||
if (moveElementsTask != null)
|
||||
{
|
||||
if (moveElementTask.Uuid.Equals(uuid))
|
||||
if (moveElementsTask.Uuids.Any(uuidMoved => uuidMoved.Equals(uuid)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -553,27 +630,36 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
|
||||
public void StartMovingElement()
|
||||
public void StartMovingElements()
|
||||
{
|
||||
ShowInsertElementButtons();
|
||||
GroupView.ListView.InvalidateViews();
|
||||
BaseAdapter adapter = (BaseAdapter)ListAdapter;
|
||||
adapter.NotifyDataSetChanged();
|
||||
|
||||
ShowInsertElementsButtons();
|
||||
BaseAdapter adapter = (BaseAdapter)ListAdapter;
|
||||
adapter.NotifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void ShowInsertElementButtons()
|
||||
public void ShowInsertElementsButtons()
|
||||
{
|
||||
GroupView.ShowInsertButtons();
|
||||
FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fabAddNewGroup).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fabAddNewEntry).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.fabAddNew).Visibility = ViewStates.Gone;
|
||||
|
||||
FindViewById(Resource.Id.bottom_bar).Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.divider2).Visibility = ViewStates.Visible;
|
||||
}
|
||||
|
||||
public void StopMovingElement()
|
||||
public void StopMovingElements()
|
||||
{
|
||||
try
|
||||
{
|
||||
MoveElementTask moveElementTask = (MoveElementTask)AppTask;
|
||||
IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(moveElementTask.Uuid, true, null);
|
||||
if (elementToMove.ParentGroup != Group)
|
||||
App.Kp2a.GetDb().Dirty.Add(elementToMove.ParentGroup);
|
||||
MoveElementsTask moveElementsTask = (MoveElementsTask)AppTask;
|
||||
foreach (var uuid in moveElementsTask.Uuids)
|
||||
{
|
||||
IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(uuid, true, null);
|
||||
if (elementToMove.ParentGroup != Group)
|
||||
App.Kp2a.GetDb().Dirty.Add(elementToMove.ParentGroup);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -583,7 +669,6 @@ namespace keepass2android
|
||||
|
||||
AppTask = new NullTask();
|
||||
AppTask.SetupGroupBaseActivityButtons(this);
|
||||
GroupView.ListView.InvalidateViews();
|
||||
BaseAdapter adapter = (BaseAdapter)ListAdapter;
|
||||
adapter.NotifyDataSetChanged();
|
||||
}
|
||||
@ -594,5 +679,186 @@ namespace keepass2android
|
||||
GroupEditActivity.Launch(this, pwGroup.ParentGroup, pwGroup);
|
||||
}
|
||||
}
|
||||
|
||||
public class GroupListFragment : ListFragment, AbsListView.IMultiChoiceModeListener
|
||||
{
|
||||
private ActionMode _mode;
|
||||
|
||||
public override void OnActivityCreated(Bundle savedInstanceState)
|
||||
{
|
||||
base.OnActivityCreated(savedInstanceState);
|
||||
if (App.Kp2a.GetDb().CanWrite)
|
||||
{
|
||||
ListView.ChoiceMode = ChoiceMode.MultipleModal;
|
||||
ListView.SetMultiChoiceModeListener(this);
|
||||
ListView.ItemLongClick += delegate(object sender, AdapterView.ItemLongClickEventArgs args)
|
||||
{
|
||||
ListView.SetItemChecked(args.Position, true);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
ListView.ItemClick += (sender, args) => ((GroupListItemView) args.View).OnClick();
|
||||
|
||||
StyleListView();
|
||||
|
||||
}
|
||||
|
||||
protected void StyleListView()
|
||||
{
|
||||
ListView lv = ListView;
|
||||
lv.ScrollBarStyle =ScrollbarStyles.InsideInset;
|
||||
lv.TextFilterEnabled = true;
|
||||
|
||||
lv.Divider = null;
|
||||
}
|
||||
|
||||
public bool OnActionItemClicked(ActionMode mode, IMenuItem item)
|
||||
{
|
||||
var listView = FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListView;
|
||||
var checkedItemPositions = listView.CheckedItemPositions;
|
||||
|
||||
List<IStructureItem> checkedItems = new List<IStructureItem>();
|
||||
for (int i = 0; i < checkedItemPositions.Size(); i++)
|
||||
{
|
||||
if (checkedItemPositions.ValueAt(i))
|
||||
{
|
||||
checkedItems.Add(((PwGroupListAdapter) ListAdapter).GetItemAtPosition(checkedItemPositions.KeyAt(i)));
|
||||
}
|
||||
}
|
||||
|
||||
//shouldn't happen, just in case...
|
||||
if (!checkedItems.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (item.ItemId)
|
||||
{
|
||||
|
||||
case Resource.Id.menu_delete:
|
||||
Handler handler = new Handler();
|
||||
DeleteMultipleItems task = new DeleteMultipleItems((GroupBaseActivity)Activity, App.Kp2a.GetDb(), checkedItems,
|
||||
new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)), App.Kp2a);
|
||||
task.Start();
|
||||
break;
|
||||
case Resource.Id.menu_move:
|
||||
var navMove = new NavigateToFolderAndLaunchMoveElementTask(checkedItems.First().ParentGroup, checkedItems.Select(i => i.Uuid).ToList(), ((GroupBaseActivity)Activity).IsSearchResult);
|
||||
((GroupBaseActivity)Activity).StartTask(navMove);
|
||||
break;
|
||||
case Resource.Id.menu_navigate:
|
||||
NavigateToFolder navNavigate = new NavigateToFolder(checkedItems.First().ParentGroup, true);
|
||||
((GroupBaseActivity)Activity).StartTask(navNavigate);
|
||||
break;
|
||||
case Resource.Id.menu_edit:
|
||||
GroupEditActivity.Launch(Activity, checkedItems.First().ParentGroup, (PwGroup) checkedItems.First());
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
listView.ClearChoices();
|
||||
((BaseAdapter)ListAdapter).NotifyDataSetChanged();
|
||||
if (_mode != null)
|
||||
mode.Finish();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnCreateActionMode(ActionMode mode, IMenu menu)
|
||||
{
|
||||
MenuInflater inflater = Activity.MenuInflater;
|
||||
inflater.Inflate(Resource.Menu.group_entriesselected, menu);
|
||||
//mode.Title = "Select Items";
|
||||
Android.Util.Log.Debug("KP2A", "Create action mode" + mode);
|
||||
((PwGroupListAdapter) ListView.Adapter).InActionMode = true;
|
||||
((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged();
|
||||
_mode = mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnDestroyActionMode(ActionMode mode)
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "Destroy action mode" + mode);
|
||||
((PwGroupListAdapter)ListView.Adapter).InActionMode = true;
|
||||
((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged();
|
||||
_mode = null;
|
||||
}
|
||||
|
||||
public bool OnPrepareActionMode(ActionMode mode, IMenu menu)
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "Prepare action mode" + mode);
|
||||
((PwGroupListAdapter)ListView.Adapter).InActionMode = mode != null;
|
||||
((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnItemCheckedStateChanged(ActionMode mode, int position, long id, bool @checked)
|
||||
{
|
||||
var menuItem = mode.Menu.FindItem(Resource.Id.menu_edit);
|
||||
if (menuItem != null)
|
||||
{
|
||||
menuItem.SetVisible(IsOnlyOneGroupChecked());
|
||||
}
|
||||
|
||||
menuItem = mode.Menu.FindItem(Resource.Id.menu_navigate);
|
||||
if (menuItem != null)
|
||||
{
|
||||
menuItem.SetVisible(((GroupBaseActivity)Activity).IsSearchResult && IsOnlyOneItemChecked());
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsOnlyOneGroupChecked()
|
||||
{
|
||||
var checkedItems = ListView.CheckedItemPositions;
|
||||
bool hadCheckedGroup = false;
|
||||
if (checkedItems != null)
|
||||
{
|
||||
for (int i = 0; i < checkedItems.Size(); i++)
|
||||
{
|
||||
if (checkedItems.ValueAt(i))
|
||||
{
|
||||
if (hadCheckedGroup)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (((PwGroupListAdapter) ListAdapter).IsGroupAtPosition(checkedItems.KeyAt(i)))
|
||||
{
|
||||
hadCheckedGroup = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hadCheckedGroup;
|
||||
}
|
||||
|
||||
private bool IsOnlyOneItemChecked()
|
||||
{
|
||||
var checkedItems = ListView.CheckedItemPositions;
|
||||
bool hadCheckedItem = false;
|
||||
if (checkedItems != null)
|
||||
{
|
||||
for (int i = 0; i < checkedItems.Size(); i++)
|
||||
{
|
||||
if (checkedItems.ValueAt(i))
|
||||
{
|
||||
if (hadCheckedItem)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
hadCheckedItem = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hadCheckedItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,12 +73,13 @@ namespace keepass2android
|
||||
SetContentView (Resource.Layout.group_edit);
|
||||
|
||||
ImageButton iconButton = (ImageButton)FindViewById (Resource.Id.icon_button);
|
||||
iconButton.SetScaleType(ImageView.ScaleType.FitXy);
|
||||
iconButton.Click += (sender, e) =>
|
||||
{
|
||||
IconPickerActivity.Launch (this);
|
||||
};
|
||||
_selectedIconId = (int) PwIcon.FolderOpen;
|
||||
iconButton.SetImageResource(Icons.IconToResId((PwIcon)_selectedIconId));
|
||||
iconButton.SetImageResource(Icons.IconToResId((PwIcon)_selectedIconId, true));
|
||||
|
||||
Button okButton = (Button)FindViewById (Resource.Id.ok);
|
||||
okButton.Click += (sender, e) => {
|
||||
@ -110,7 +111,7 @@ namespace keepass2android
|
||||
_selectedCustomIconId = _groupToEdit.CustomIconUuid;
|
||||
TextView nameField = (TextView)FindViewById(Resource.Id.group_name);
|
||||
nameField.Text = _groupToEdit.Name;
|
||||
App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iconButton, Resources, App.Kp2a.GetDb().KpDatabase, _groupToEdit.IconId, _groupToEdit.CustomIconUuid);
|
||||
App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iconButton, Resources, App.Kp2a.GetDb().KpDatabase, _groupToEdit.IconId, _groupToEdit.CustomIconUuid, false);
|
||||
SetTitle(Resource.String.edit_group_title);
|
||||
}
|
||||
else
|
||||
@ -137,7 +138,7 @@ namespace keepass2android
|
||||
_selectedIconId = data.Extras.GetInt(IconPickerActivity.KeyIconId);
|
||||
_selectedCustomIconId = PwUuid.Zero;
|
||||
ImageButton currIconButton = (ImageButton) FindViewById(Resource.Id.icon_button);
|
||||
currIconButton.SetImageResource(Icons.IconToResId((PwIcon)_selectedIconId));
|
||||
currIconButton.SetImageResource(Icons.IconToResId((PwIcon)_selectedIconId, false));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,14 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.OS;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Activity (Label = "@string/app_name", Theme="@style/NoTitleBar")]
|
||||
[Activity(Label = "@string/app_name", Theme = "@style/MyTheme_ActionBar")]
|
||||
public class IconPickerActivity : LockCloseActivity
|
||||
{
|
||||
public const String KeyIconId = "icon_id";
|
||||
@ -100,8 +101,10 @@ namespace keepass2android
|
||||
TextView tv = (TextView) currView.FindViewById(Resource.Id.icon_text);
|
||||
tv.Text = "" + position;
|
||||
ImageView iv = (ImageView) currView.FindViewById(Resource.Id.icon_image);
|
||||
iv.SetImageResource(Icons.IconToResId((KeePassLib.PwIcon)position));
|
||||
|
||||
iv.SetImageResource(Icons.IconToResId((KeePassLib.PwIcon)position, false));
|
||||
Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop;
|
||||
Color color = new Color(189, 189, 189);
|
||||
iv.SetColorFilter(color, mMode);
|
||||
return currView;
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ namespace keepass2android
|
||||
/// <summary>
|
||||
/// Launcher activity of Keepass2Android. This activity usually forwards to FileSelect but may show the revision dialog after installation or updates.
|
||||
/// </summary>
|
||||
[Activity (Label = AppNames.AppName, MainLauncher = true, Theme="@style/Base")]
|
||||
[Activity(Label = AppNames.AppName, MainLauncher = true, Theme = "@style/MyTheme_Blue")]
|
||||
public class KeePass : LifecycleDebugActivity
|
||||
{
|
||||
public const Result ExitNormal = Result.FirstUser;
|
||||
@ -167,6 +167,9 @@ namespace keepass2android
|
||||
{
|
||||
|
||||
}
|
||||
#if DEBUG
|
||||
showChangeLog = false;
|
||||
#endif
|
||||
|
||||
if (showChangeLog)
|
||||
{
|
||||
|
@ -19,11 +19,12 @@ using System;
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Support.V7.App;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
|
||||
public abstract class LifecycleDebugActivity : Activity
|
||||
public abstract class LifecycleDebugActivity : AppCompatActivity
|
||||
{
|
||||
protected LifecycleDebugActivity (IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
|
@ -16,14 +16,119 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Android.Content.Res;
|
||||
using Android.Graphics;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Preferences;
|
||||
using Android.Support.V7.App;
|
||||
using Android.Views;
|
||||
using Java.Lang;
|
||||
using keepass2android;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
|
||||
public class AppCompatPreferenceActivity: PreferenceActivity
|
||||
{
|
||||
public AppCompatPreferenceActivity(IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AppCompatPreferenceActivity()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private AppCompatDelegate _appCompatDelegate;
|
||||
|
||||
AppCompatDelegate Delegate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_appCompatDelegate == null)
|
||||
_appCompatDelegate = AppCompatDelegate.Create(this, null);
|
||||
return _appCompatDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
Delegate.InstallViewFactory();
|
||||
Delegate.OnCreate(savedInstanceState);
|
||||
base.OnCreate(savedInstanceState);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override MenuInflater MenuInflater
|
||||
{
|
||||
get { return Delegate.MenuInflater; }
|
||||
}
|
||||
|
||||
|
||||
public override void SetContentView(int layoutResId)
|
||||
{
|
||||
Delegate.SetContentView(layoutResId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override void SetContentView(View view) {
|
||||
Delegate.SetContentView(view);
|
||||
}
|
||||
|
||||
public override void SetContentView(View view, ViewGroup.LayoutParams @params) {
|
||||
Delegate.SetContentView(view, @params);
|
||||
}
|
||||
|
||||
public override void AddContentView(View view, ViewGroup.LayoutParams @params) {
|
||||
Delegate.AddContentView(view, @params);
|
||||
}
|
||||
|
||||
protected override void OnPostResume()
|
||||
{
|
||||
base.OnPostResume();
|
||||
Delegate.OnPostResume();
|
||||
}
|
||||
|
||||
protected override void OnTitleChanged(ICharSequence title, Color color)
|
||||
{
|
||||
base.OnTitleChanged(title, color);
|
||||
Delegate.SetTitle(title);
|
||||
}
|
||||
|
||||
|
||||
public override void OnConfigurationChanged(Configuration newConfig)
|
||||
{
|
||||
base.OnConfigurationChanged(newConfig);
|
||||
Delegate.OnConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
base.OnStop();
|
||||
Delegate.OnStop();
|
||||
}
|
||||
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
Delegate.OnDestroy();
|
||||
}
|
||||
|
||||
public override void InvalidateOptionsMenu()
|
||||
{
|
||||
Delegate.InvalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class LockingPreferenceActivity : PreferenceActivity {
|
||||
public class LockingPreferenceActivity : AppCompatPreferenceActivity {
|
||||
|
||||
public LockingPreferenceActivity (IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
|
64
src/keepass2android/MeasuringRelativeLayout.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using Android.Content;
|
||||
using Android.Runtime;
|
||||
using Android.Util;
|
||||
using Android.Widget;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
public class MeasuringRelativeLayout : RelativeLayout
|
||||
{
|
||||
protected MeasuringRelativeLayout(IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
|
||||
public MeasuringRelativeLayout(Context context)
|
||||
: base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public MeasuringRelativeLayout(Context context, IAttributeSet attrs)
|
||||
: base(context, attrs)
|
||||
{
|
||||
}
|
||||
|
||||
public MeasuringRelativeLayout(Context context, IAttributeSet attrs, int defStyleAttr)
|
||||
: base(context, attrs, defStyleAttr)
|
||||
{
|
||||
}
|
||||
|
||||
public MeasuringRelativeLayout(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes)
|
||||
: base(context, attrs, defStyleAttr, defStyleRes)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public class MeasureArgs
|
||||
{
|
||||
public int ActualHeight;
|
||||
public int ProposedHeight;
|
||||
|
||||
}
|
||||
|
||||
public event EventHandler<MeasureArgs> MeasureEvent;
|
||||
|
||||
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
MeasureArgs args = new MeasureArgs();
|
||||
|
||||
args.ProposedHeight = MeasureSpec.GetSize(heightMeasureSpec);
|
||||
args.ActualHeight = Height;
|
||||
|
||||
|
||||
OnMeasureEvent(args);
|
||||
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
protected virtual void OnMeasureEvent(MeasureArgs args)
|
||||
{
|
||||
var handler = MeasureEvent;
|
||||
if (handler != null) handler(this, args);
|
||||
}
|
||||
}
|
||||
}
|
@ -34,6 +34,11 @@ using Java.Net;
|
||||
using Android.Preferences;
|
||||
using Android.Text;
|
||||
using Android.Content.PM;
|
||||
using Android.Graphics;
|
||||
using Android.Support.Design.Widget;
|
||||
using Android.Support.V4.Widget;
|
||||
using Android.Support.V7.App;
|
||||
using keepass2android;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
using KeePassLib.Utility;
|
||||
@ -41,22 +46,25 @@ using Keepass2android.Pluginsdk;
|
||||
using OtpKeyProv;
|
||||
using keepass2android.Io;
|
||||
using keepass2android.Utils;
|
||||
using Exception = System.Exception;
|
||||
|
||||
using File = Java.IO.File;
|
||||
using FileNotFoundException = Java.IO.FileNotFoundException;
|
||||
using MemoryStream = System.IO.MemoryStream;
|
||||
|
||||
using Object = Java.Lang.Object;
|
||||
using Process = Android.OS.Process;
|
||||
using String = System.String;
|
||||
|
||||
using KeeChallenge;
|
||||
using AlertDialog = Android.App.AlertDialog;
|
||||
using Toolbar = Android.Support.V7.Widget.Toolbar;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Activity(Label = "@string/app_name",
|
||||
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
|
||||
ConfigurationChanges = ConfigChanges.Orientation,
|
||||
LaunchMode = LaunchMode.SingleInstance,
|
||||
Theme = "@style/Base")]
|
||||
public class PasswordActivity : LockingActivity {
|
||||
WindowSoftInputMode = SoftInput.AdjustResize,
|
||||
Theme = "@style/MyTheme_Blue")] /*caution: also contained in AndroidManifest.xml*/
|
||||
public class PasswordActivity : LockingActivity {
|
||||
|
||||
enum KeyProviders
|
||||
{
|
||||
@ -141,20 +149,23 @@ namespace keepass2android
|
||||
private const string KeyFileOrProviderKey = "KeyFileOrProviderKey";
|
||||
|
||||
|
||||
private ActivityDesign _design;
|
||||
private bool _performingLoad;
|
||||
private bool _keepPasswordInOnResume;
|
||||
private Typeface _passwordFont;
|
||||
|
||||
private ActionBarDrawerToggle mDrawerToggle;
|
||||
private DrawerLayout _drawerLayout;
|
||||
|
||||
|
||||
public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer)
|
||||
public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
{
|
||||
_design = new ActivityDesign(this);
|
||||
|
||||
}
|
||||
|
||||
public PasswordActivity()
|
||||
{
|
||||
_design = new ActivityDesign(this);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -300,6 +311,8 @@ namespace keepass2android
|
||||
{
|
||||
if (KeyProviderType == KeyProviders.KeyFile)
|
||||
{
|
||||
//TODO: if the user has not yet selected a keyfile, _keyFileOrProvider is empty which
|
||||
//gives an (unhandled) exception here
|
||||
var iocKeyfile = IOConnectionInfo.UnserializeFromString(_keyFileOrProvider);
|
||||
|
||||
App.Kp2a.GetFileStorage(iocKeyfile)
|
||||
@ -666,11 +679,68 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
int count = 1;
|
||||
|
||||
private DrawerLayout mDrawerLayout;
|
||||
//private RecyclerView mDrawerList;
|
||||
|
||||
private string mDrawerTitle;
|
||||
private MeasuringRelativeLayout.MeasureArgs _measureArgs;
|
||||
internal class MyActionBarDrawerToggle : ActionBarDrawerToggle
|
||||
{
|
||||
PasswordActivity owner;
|
||||
|
||||
public MyActionBarDrawerToggle(PasswordActivity activity, DrawerLayout layout, int imgRes, int openRes, int closeRes)
|
||||
: base(activity, layout, openRes, closeRes)
|
||||
{
|
||||
owner = activity;
|
||||
}
|
||||
|
||||
public override void OnDrawerClosed(View drawerView)
|
||||
{
|
||||
owner.SupportActionBar.Title = owner.Title;
|
||||
owner.InvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
public override void OnDrawerOpened(View drawerView)
|
||||
{
|
||||
owner.SupportActionBar.Title = owner.mDrawerTitle;
|
||||
owner.InvalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
private void UncollapseToolbar()
|
||||
{
|
||||
AppBarLayout appbarLayout = FindViewById<AppBarLayout>(Resource.Id.appbar);
|
||||
var tmp = appbarLayout.LayoutParameters;
|
||||
CoordinatorLayout.LayoutParams p = tmp.JavaCast<CoordinatorLayout.LayoutParams>();
|
||||
var tmp2 = p.Behavior;
|
||||
var behavior = tmp2.JavaCast<AppBarLayout.Behavior>();
|
||||
if (behavior == null)
|
||||
{
|
||||
p.Behavior = behavior = new AppBarLayout.Behavior();
|
||||
}
|
||||
behavior.OnNestedFling(FindViewById<CoordinatorLayout>(Resource.Id.main_content), appbarLayout, null, 0, -10000, false);
|
||||
}
|
||||
|
||||
private void CollapseToolbar()
|
||||
{
|
||||
AppBarLayout appbarLayout = FindViewById<AppBarLayout>(Resource.Id.appbar);
|
||||
ViewGroup.LayoutParams tmp = appbarLayout.LayoutParameters;
|
||||
CoordinatorLayout.LayoutParams p = tmp.JavaCast<CoordinatorLayout.LayoutParams>();
|
||||
var tmp2 = p.Behavior;
|
||||
var behavior = tmp2.JavaCast<AppBarLayout.Behavior>();
|
||||
if (behavior == null)
|
||||
{
|
||||
p.Behavior = behavior = new AppBarLayout.Behavior();
|
||||
}
|
||||
behavior.OnNestedFling(FindViewById<CoordinatorLayout>(Resource.Id.main_content), appbarLayout, null, 0, 200, true);
|
||||
}
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
_design.ApplyTheme();
|
||||
|
||||
|
||||
//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))
|
||||
@ -678,8 +748,6 @@ namespace keepass2android
|
||||
Window.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Intent i = Intent;
|
||||
|
||||
//only load the AppTask if this is the "first" OnCreate (not because of kill/resume, i.e. savedInstanceState==null)
|
||||
@ -692,12 +760,12 @@ namespace keepass2android
|
||||
}
|
||||
else
|
||||
{
|
||||
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
|
||||
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
String action = i.Action;
|
||||
|
||||
|
||||
_prefs = PreferenceManager.GetDefaultSharedPreferences(this);
|
||||
_rememberKeyfile = _prefs.GetBoolean(GetString(Resource.String.keyfile_key), Resources.GetBoolean(Resource.Boolean.keyfile_default));
|
||||
|
||||
@ -707,7 +775,7 @@ namespace keepass2android
|
||||
if (action != null && action.Equals(ViewIntent))
|
||||
{
|
||||
if (!GetIocFromViewIntent(i)) return;
|
||||
}
|
||||
}
|
||||
else if ((action != null) && (action.Equals(Intents.StartWithOtp)))
|
||||
{
|
||||
if (!GetIocFromOtpIntent(savedInstanceState, i)) return;
|
||||
@ -745,46 +813,49 @@ namespace keepass2android
|
||||
App.Kp2a.LockDatabase(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
SetContentView(Resource.Layout.password);
|
||||
|
||||
InitializeToolbar();
|
||||
|
||||
InitializeFilenameView();
|
||||
|
||||
if (KeyProviderType == KeyProviders.KeyFile)
|
||||
{
|
||||
UpdateKeyfileIocView();
|
||||
}
|
||||
|
||||
|
||||
|
||||
FindViewById<EditText>(Resource.Id.password).TextChanged +=
|
||||
var passwordEdit = FindViewById<EditText>(Resource.Id.password_edit);
|
||||
passwordEdit.TextChanged +=
|
||||
(sender, args) =>
|
||||
{
|
||||
_password = FindViewById<EditText>(Resource.Id.password).Text;
|
||||
_password = passwordEdit.Text;
|
||||
UpdateOkButtonState();
|
||||
};
|
||||
FindViewById<EditText>(Resource.Id.password).EditorAction += (sender, args) =>
|
||||
{
|
||||
if ((args.ActionId == ImeAction.Done) || ((args.ActionId == ImeAction.ImeNull) && (args.Event.Action == KeyEventActions.Down)))
|
||||
OnOk();
|
||||
};
|
||||
passwordEdit.EditorAction += (sender, args) =>
|
||||
{
|
||||
if ((args.ActionId == ImeAction.Done) || ((args.ActionId == ImeAction.ImeNull) && (args.Event.Action == KeyEventActions.Down)))
|
||||
OnOk();
|
||||
};
|
||||
|
||||
|
||||
FindViewById<EditText>(Resource.Id.pass_otpsecret).TextChanged += (sender, args) => UpdateOkButtonState();
|
||||
|
||||
|
||||
EditText passwordEdit = FindViewById<EditText>(Resource.Id.password);
|
||||
passwordEdit.Text = _password;
|
||||
passwordEdit.RequestFocus();
|
||||
Window.SetSoftInputMode(SoftInput.StateVisible);
|
||||
|
||||
InitializeOkButton();
|
||||
var passwordFont = Typeface.CreateFromAsset(Assets, "SourceCodePro-Regular.ttf");
|
||||
passwordEdit.Typeface = passwordFont;
|
||||
|
||||
|
||||
InitializeBottomBarButtons();
|
||||
|
||||
InitializePasswordModeSpinner();
|
||||
|
||||
InitializeOtpSecretSpinner();
|
||||
|
||||
InitializeNavDrawerButtons();
|
||||
|
||||
UpdateOkButtonState();
|
||||
|
||||
|
||||
InitializeTogglePasswordButton();
|
||||
InitializeKeyfileBrowseButton();
|
||||
|
||||
@ -798,9 +869,91 @@ namespace keepass2android
|
||||
.PrepareFileUsage(new FileStorageSetupInitiatorActivity(this, OnActivityResult, null), _ioConnection,
|
||||
RequestCodePrepareDbFile, false);
|
||||
}
|
||||
|
||||
|
||||
mDrawerTitle = this.Title;
|
||||
mDrawerLayout = FindViewById<DrawerLayout>(Resource.Id.drawer_layout);
|
||||
var rootview = FindViewById<MeasuringRelativeLayout>(Resource.Id.relative_layout);
|
||||
rootview.ViewTreeObserver.GlobalLayout += (sender, args2) =>
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "GlobalLayout");
|
||||
var args = _measureArgs;
|
||||
if (args == null)
|
||||
return;
|
||||
Android.Util.Log.Debug("KP2A", "ActualHeight=" + args.ActualHeight);
|
||||
Android.Util.Log.Debug("KP2A", "ProposedHeight=" + args.ProposedHeight);
|
||||
if (args.ActualHeight < args.ProposedHeight)
|
||||
UncollapseToolbar();
|
||||
if (args.ActualHeight > args.ProposedHeight)
|
||||
CollapseToolbar();
|
||||
};
|
||||
rootview.MeasureEvent += (sender, args) =>
|
||||
{
|
||||
//Snackbar.Make(rootview, "height="+args.ActualHeight, Snackbar.LengthLong).Show();
|
||||
this._measureArgs = args;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private void InitializeOtpSecretSpinner()
|
||||
private void InitializeNavDrawerButtons()
|
||||
{
|
||||
FindViewById(Resource.Id.btn_nav_change_db).Click += (sender, args) =>
|
||||
{
|
||||
GoToFileSelectActivity();
|
||||
};
|
||||
|
||||
FindViewById(Resource.Id.btn_nav_donate).Click += (sender, args) =>
|
||||
{
|
||||
Util.GotoDonateUrl(this);
|
||||
};
|
||||
FindViewById(Resource.Id.btn_nav_about).Click += (sender, args) =>
|
||||
{
|
||||
AboutDialog dialog = new AboutDialog(this);
|
||||
dialog.Show();
|
||||
};
|
||||
|
||||
FindViewById(Resource.Id.btn_nav_settings).Click += (sender, args) =>
|
||||
{
|
||||
AppSettingsActivity.Launch(this);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private void InitializeToolbar()
|
||||
{
|
||||
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar);
|
||||
|
||||
SetSupportActionBar(toolbar);
|
||||
|
||||
var collapsingToolbar = FindViewById<CollapsingToolbarLayout>(Resource.Id.collapsing_toolbar);
|
||||
collapsingToolbar.SetTitle(GetString(Resource.String.unlock_database_title));
|
||||
|
||||
_drawerLayout = FindViewById<DrawerLayout>(Resource.Id.drawer_layout);
|
||||
mDrawerToggle = new ActionBarDrawerToggle(this, _drawerLayout,
|
||||
Resource.String.menu_open,
|
||||
Resource.String.menu_close);
|
||||
|
||||
|
||||
_drawerLayout.SetDrawerListener(mDrawerToggle);
|
||||
|
||||
|
||||
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
|
||||
SupportActionBar.SetHomeButtonEnabled(true);
|
||||
mDrawerToggle.SyncState();
|
||||
|
||||
}
|
||||
|
||||
public override void OnBackPressed()
|
||||
{
|
||||
if (_drawerLayout.IsDrawerOpen((int) GravityFlags.Start))
|
||||
{
|
||||
_drawerLayout.CloseDrawer((int)GravityFlags.Start);
|
||||
return;
|
||||
}
|
||||
base.OnBackPressed();
|
||||
}
|
||||
|
||||
private void InitializeOtpSecretSpinner()
|
||||
{
|
||||
Spinner spinner = FindViewById<Spinner>(Resource.Id.otpsecret_format_spinner);
|
||||
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(this, Android.Resource.Layout.SimpleSpinnerDropDownItem, EncodingUtil.Formats);
|
||||
@ -892,13 +1045,15 @@ namespace keepass2android
|
||||
return true;
|
||||
}
|
||||
|
||||
private void InitializeOkButton()
|
||||
private void InitializeBottomBarButtons()
|
||||
{
|
||||
Button confirmButton = (Button) FindViewById(Resource.Id.pass_ok);
|
||||
confirmButton.Click += (sender, e) =>
|
||||
{
|
||||
OnOk();
|
||||
};
|
||||
|
||||
FindViewById(Resource.Id.change_db).Click += (sender, args) => GoToFileSelectActivity();
|
||||
}
|
||||
|
||||
private void OnOk()
|
||||
@ -916,6 +1071,9 @@ namespace keepass2android
|
||||
_showPassword = !_showPassword;
|
||||
MakePasswordMaskedOrVisible();
|
||||
};
|
||||
Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop;
|
||||
Color color = new Color (224, 224, 224);
|
||||
btnTogglePassword.SetColorFilter (color, mMode);
|
||||
}
|
||||
|
||||
private void InitializeKeyfileBrowseButton()
|
||||
@ -974,11 +1132,7 @@ namespace keepass2android
|
||||
RequestCodePrepareOtpAuxFile, false);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
//android 2.x
|
||||
//TODO test
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void RestoreState(Bundle savedInstanceState)
|
||||
@ -989,7 +1143,7 @@ namespace keepass2android
|
||||
MakePasswordMaskedOrVisible();
|
||||
|
||||
_keyFileOrProvider = savedInstanceState.GetString(KeyFileOrProviderKey);
|
||||
_password = FindViewById<EditText>(Resource.Id.password).Text = savedInstanceState.GetString(PasswordKey);
|
||||
_password = FindViewById<EditText>(Resource.Id.password_edit).Text = savedInstanceState.GetString(PasswordKey);
|
||||
|
||||
_pendingOtps = new List<string>(savedInstanceState.GetStringArrayList(PendingOtpsKey));
|
||||
|
||||
@ -1012,17 +1166,18 @@ namespace keepass2android
|
||||
|
||||
private void UpdateOkButtonState()
|
||||
{
|
||||
bool enabled = false;
|
||||
switch (KeyProviderType)
|
||||
{
|
||||
case KeyProviders.None:
|
||||
FindViewById(Resource.Id.pass_ok).Enabled = true;
|
||||
enabled = true;
|
||||
break;
|
||||
case KeyProviders.KeyFile:
|
||||
FindViewById(Resource.Id.pass_ok).Enabled = _keyFileOrProvider != "" || _password != "";
|
||||
enabled = _keyFileOrProvider != "" || _password != "";
|
||||
break;
|
||||
case KeyProviders.Otp:
|
||||
|
||||
bool enabled = true;
|
||||
enabled = true;
|
||||
if (_otpInfo == null)
|
||||
enabled = false;
|
||||
else
|
||||
@ -1040,18 +1195,20 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
FindViewById(Resource.Id.pass_ok).Enabled = enabled;
|
||||
|
||||
break;
|
||||
case KeyProviders.OtpRecovery:
|
||||
case KeyProviders.ChalRecovery:
|
||||
FindViewById(Resource.Id.pass_ok).Enabled = FindViewById<EditText>(Resource.Id.pass_otpsecret).Text != "";
|
||||
enabled = FindViewById<EditText>(Resource.Id.pass_otpsecret).Text != "";
|
||||
break;
|
||||
case KeyProviders.Challenge:
|
||||
FindViewById(Resource.Id.pass_ok).Enabled = _challengeSecret != null;
|
||||
enabled = _challengeSecret != null;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
FindViewById(Resource.Id.pass_ok).Enabled = enabled;
|
||||
|
||||
}
|
||||
|
||||
private void UpdateKeyProviderUiState()
|
||||
@ -1212,18 +1369,29 @@ namespace keepass2android
|
||||
|
||||
private void MakePasswordMaskedOrVisible()
|
||||
{
|
||||
TextView password = (TextView) FindViewById(Resource.Id.password);
|
||||
TextView password = (TextView) FindViewById(Resource.Id.password_edit);
|
||||
if (_showPassword)
|
||||
{
|
||||
password.InputType = InputTypes.ClassText | InputTypes.TextVariationVisiblePassword;
|
||||
SetPasswordTypeface(password);
|
||||
}
|
||||
else
|
||||
{
|
||||
password.InputType = InputTypes.ClassText | InputTypes.TextVariationPassword;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void SetNewDefaultFile()
|
||||
private void SetPasswordTypeface(TextView textView)
|
||||
{
|
||||
if (_passwordFont == null)
|
||||
{
|
||||
_passwordFont = Typeface.CreateFromAsset(Assets, "SourceCodePro-Regular.ttf");
|
||||
}
|
||||
textView.Typeface = _passwordFont;
|
||||
}
|
||||
|
||||
private void SetNewDefaultFile()
|
||||
{
|
||||
//Don't allow the current file to be the default if we don't have stored credentials
|
||||
bool makeFileDefault;
|
||||
@ -1331,7 +1499,7 @@ namespace keepass2android
|
||||
|
||||
outState.PutString(OtpInfoKey, sw.ToString());
|
||||
}
|
||||
|
||||
|
||||
//more OTP TODO:
|
||||
// * Caching of aux file
|
||||
// * -> implement IFileStorage in JavaFileStorage based on ListFiles
|
||||
@ -1390,7 +1558,7 @@ namespace keepass2android
|
||||
else
|
||||
{
|
||||
//assume the key should be used as static password
|
||||
FindViewById<EditText>(Resource.Id.password).Text += otp;
|
||||
FindViewById<EditText>(Resource.Id.password_edit).Text += otp;
|
||||
}
|
||||
|
||||
|
||||
@ -1402,7 +1570,12 @@ namespace keepass2android
|
||||
{
|
||||
base.OnResume();
|
||||
|
||||
_design.ReapplyTheme();
|
||||
EditText pwd = FindViewById<EditText>(Resource.Id.password_edit);
|
||||
pwd.PostDelayed(() =>
|
||||
{
|
||||
InputMethodManager keyboard = (InputMethodManager)GetSystemService(Context.InputMethodService);
|
||||
keyboard.ShowSoftInput(pwd, 0);
|
||||
}, 50);
|
||||
|
||||
View killButton = FindViewById(Resource.Id.kill_app);
|
||||
if (PreferenceManager.GetDefaultSharedPreferences(this)
|
||||
@ -1421,7 +1594,7 @@ namespace keepass2android
|
||||
{
|
||||
killButton.Visibility = ViewStates.Gone;
|
||||
}
|
||||
|
||||
|
||||
if (!_keepPasswordInOnResume)
|
||||
{
|
||||
if (
|
||||
@ -1514,15 +1687,7 @@ namespace keepass2android
|
||||
|
||||
private void InitializeFilenameView() {
|
||||
SetEditText(Resource.Id.filename, App.Kp2a.GetFileStorage(_ioConnection).GetDisplayName(_ioConnection));
|
||||
if (App.Kp2a.FileDbHelper.NumberOfRecentFiles() < 2)
|
||||
{
|
||||
FindViewById(Resource.Id.filename_group).Visibility = ViewStates.Gone;
|
||||
}
|
||||
else
|
||||
{
|
||||
FindViewById(Resource.Id.filename_group).Visibility = ViewStates.Visible;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
@ -1547,30 +1712,13 @@ namespace keepass2android
|
||||
te.Text = str;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnCreateOptionsMenu(IMenu menu) {
|
||||
base.OnCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflate = MenuInflater;
|
||||
inflate.Inflate(Resource.Menu.password, menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool OnOptionsItemSelected(IMenuItem item) {
|
||||
switch ( item.ItemId ) {
|
||||
case Resource.Id.menu_about:
|
||||
AboutDialog dialog = new AboutDialog(this);
|
||||
dialog.Show();
|
||||
return true;
|
||||
|
||||
case Resource.Id.menu_app_settings:
|
||||
AppSettingsActivity.Launch(this);
|
||||
return true;
|
||||
|
||||
case Resource.Id.menu_change_db:
|
||||
GoToFileSelectActivity();
|
||||
return true;
|
||||
case Android.Resource.Id.Home:
|
||||
_drawerLayout.OpenDrawer(Android.Support.V4.View.GravityCompat.Start);
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnOptionsItemSelected(item);
|
||||
@ -1643,7 +1791,7 @@ namespace keepass2android
|
||||
|
||||
private void ClearEnteredPassword()
|
||||
{
|
||||
SetEditText(Resource.Id.password, "");
|
||||
SetEditText(Resource.Id.password_edit, "");
|
||||
SetEditText(Resource.Id.pass_otpsecret, "");
|
||||
foreach (int otpId in _otpTextViewIds)
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="46" android:versionName="0.9.3-release-3" package="keepass2android.keepass2android_debug" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
|
||||
<permission android:description="@string/permission_desc" android:icon="@drawable/ic_launcher" android:label="KP2A internal file browsing" android:name="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" android:protectionLevel="signature" />
|
||||
<application android:label="keepass2android" android:icon="@drawable/ic_launcher">
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:theme="@style/Base" android:name="keepass2android.PasswordActivity">
|
||||
<activity android:configChanges="orientation" android:label="@string/app_name" android:theme="@style/MyTheme_Blue" android:name="keepass2android.PasswordActivity" android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -11,7 +11,7 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:theme="@style/Base" android:name="keepass2android.PasswordActivity">
|
||||
<activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:theme="@style/MyTheme" android:name="keepass2android.PasswordActivity">
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="59"
|
||||
android:versionName="0.9.8b"
|
||||
android:versionCode="57"
|
||||
android:versionName="0.9.8 preview 2 (alpha)"
|
||||
package="keepass2android.keepass2android"
|
||||
android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
|
||||
@ -57,7 +57,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:theme="@style/Base" android:name="keepass2android.PasswordActivity">
|
||||
<activity android:configChanges="orientation" android:label="@string/app_name" android:theme="@style/MyTheme_Blue" android:name="keepass2android.PasswordActivity" android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -40,7 +40,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:theme="@style/Base" android:name="keepass2android.PasswordActivity">
|
||||
<activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:theme="@style/MyTheme" android:name="keepass2android.PasswordActivity" android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -24,6 +24,7 @@ using Android.Widget;
|
||||
using Android.Preferences;
|
||||
using KeePassLib;
|
||||
using keepass2android.view;
|
||||
using KeePassLib.Interfaces;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
@ -212,6 +213,7 @@ namespace keepass2android
|
||||
private List<PwGroup> _groupsForViewing;
|
||||
private List<PwEntry> _entriesForViewing;
|
||||
|
||||
public bool InActionMode { get; set; }
|
||||
|
||||
|
||||
public PwGroupListAdapter(GroupBaseActivity act, PwGroup group) {
|
||||
@ -275,6 +277,11 @@ namespace keepass2android
|
||||
public override Java.Lang.Object GetItem(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public bool IsGroupAtPosition(int position)
|
||||
{
|
||||
return position < _groupsForViewing.Count;
|
||||
}
|
||||
|
||||
public override long GetItemId(int position) {
|
||||
return position;
|
||||
@ -282,15 +289,18 @@ namespace keepass2android
|
||||
|
||||
public override View GetView(int position, View convertView, ViewGroup parent) {
|
||||
int size = _groupsForViewing.Count;
|
||||
|
||||
GroupListItemView view;
|
||||
if ( position < size ) {
|
||||
return CreateGroupView(position, convertView);
|
||||
view = CreateGroupView(position, convertView);
|
||||
} else {
|
||||
return CreateEntryView(position - size, convertView);
|
||||
view = CreateEntryView(position - size, convertView);
|
||||
}
|
||||
view.SetRightArrowVisibility(!InActionMode);
|
||||
return view;
|
||||
|
||||
}
|
||||
|
||||
private View CreateGroupView(int position, View convertView) {
|
||||
private PwGroupView CreateGroupView(int position, View convertView) {
|
||||
PwGroup g = _groupsForViewing[position];
|
||||
PwGroupView gv;
|
||||
|
||||
@ -321,7 +331,18 @@ namespace keepass2android
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
|
||||
public IStructureItem GetItemAtPosition(int keyAt)
|
||||
{
|
||||
if (keyAt < _groupsForViewing.Count)
|
||||
{
|
||||
return _groupsForViewing[keyAt];
|
||||
}
|
||||
else
|
||||
{
|
||||
return _entriesForViewing[keyAt - _groupsForViewing.Count];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ namespace keepass2android
|
||||
{
|
||||
[Activity(Label = "@string/app_name",
|
||||
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
|
||||
Theme = "@style/Base")]
|
||||
Theme = "@style/MyTheme_ActionBar")]
|
||||
[IntentFilter(new[] { Strings.ActionQueryCredentials},
|
||||
Categories = new[] { Intent.CategoryDefault })]
|
||||
[IntentFilter(new[] { Strings.ActionQueryCredentialsForOwnPackage },
|
||||
|
@ -24,30 +24,31 @@ using Android.Widget;
|
||||
using Android.Content.PM;
|
||||
using KeePassLib.Keys;
|
||||
using Android.Preferences;
|
||||
using Android.Runtime;
|
||||
using Android.Support.Design.Widget;
|
||||
using Android.Views.InputMethods;
|
||||
using KeePassLib.Serialization;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
|
||||
Theme = "@style/Base")]
|
||||
[Activity(Label = "@string/app_name",
|
||||
ConfigurationChanges = ConfigChanges.Orientation,
|
||||
WindowSoftInputMode = SoftInput.AdjustResize,
|
||||
MainLauncher = false,
|
||||
Theme = "@style/MyTheme_Blue")]
|
||||
public class QuickUnlock : LifecycleDebugActivity
|
||||
{
|
||||
private IOConnectionInfo _ioc;
|
||||
private QuickUnlockBroadcastReceiver _intentReceiver;
|
||||
|
||||
private ActivityDesign _design;
|
||||
|
||||
public QuickUnlock()
|
||||
{
|
||||
_design = new ActivityDesign(this);
|
||||
}
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
_design.ApplyTheme();
|
||||
|
||||
|
||||
//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))
|
||||
@ -63,13 +64,12 @@ namespace keepass2android
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
SetContentView(Resource.Layout.QuickUnlock);
|
||||
|
||||
|
||||
if (App.Kp2a.GetDb().KpDatabase.Name != "")
|
||||
{
|
||||
FindViewById(Resource.Id.filename_label).Visibility = ViewStates.Invisible;
|
||||
((TextView) FindViewById(Resource.Id.qu_filename)).Text = App.Kp2a.GetDb().KpDatabase.Name;
|
||||
FindViewById(Resource.Id.filename_label).Visibility = ViewStates.Visible;
|
||||
((TextView) FindViewById(Resource.Id.filename_label)).Text = App.Kp2a.GetDb().KpDatabase.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -78,11 +78,11 @@ namespace keepass2android
|
||||
.GetBoolean(GetString(Resource.String.RememberRecentFiles_key),
|
||||
Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default)))
|
||||
{
|
||||
((TextView) FindViewById(Resource.Id.qu_filename)).Text = App.Kp2a.GetFileStorage(_ioc).GetDisplayName(_ioc);
|
||||
((TextView) FindViewById(Resource.Id.filename_label)).Text = App.Kp2a.GetFileStorage(_ioc).GetDisplayName(_ioc);
|
||||
}
|
||||
else
|
||||
{
|
||||
((TextView) FindViewById(Resource.Id.qu_filename)).Text = "*****";
|
||||
((TextView) FindViewById(Resource.Id.filename_label)).Text = "*****";
|
||||
}
|
||||
|
||||
}
|
||||
@ -104,6 +104,8 @@ namespace keepass2android
|
||||
OnUnlock(quickUnlockLength, pwd);
|
||||
};
|
||||
|
||||
|
||||
|
||||
Button btnLock = (Button) FindViewById(Resource.Id.QuickUnlock_buttonLock);
|
||||
btnLock.Click += (object sender, EventArgs e) =>
|
||||
{
|
||||
@ -120,8 +122,10 @@ namespace keepass2android
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.AddAction(Intents.DatabaseLocked);
|
||||
RegisterReceiver(_intentReceiver, filter);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void OnUnlock(int quickUnlockLength, EditText pwd)
|
||||
{
|
||||
KcpPassword kcpPassword = (KcpPassword) App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof (KcpPassword));
|
||||
@ -148,9 +152,7 @@ namespace keepass2android
|
||||
protected override void OnResume()
|
||||
{
|
||||
base.OnResume();
|
||||
|
||||
_design.ReapplyTheme();
|
||||
|
||||
|
||||
CheckIfUnloaded();
|
||||
|
||||
EditText pwd = (EditText) FindViewById(Resource.Id.QuickUnlock_password);
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false">
|
||||
<translate android:fromXDelta="0%" android:toXDelta="-100%"
|
||||
<translate android:fromXDelta="0%" android:toXDelta="0%"
|
||||
android:fromYDelta="0%" android:toYDelta="0%"
|
||||
android:duration="400"/>
|
||||
</set>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false">
|
||||
<translate
|
||||
android:fromXDelta="0%" android:toXDelta="100%"
|
||||
android:fromXDelta="0%" android:toXDelta="0%"
|
||||
android:fromYDelta="0%" android:toYDelta="0%"
|
||||
android:duration="250" />
|
||||
</set>
|
||||
|
Before Width: | Height: | Size: 895 B |
Before Width: | Height: | Size: 961 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 4.2 KiB |