Refactoring: Introduced "AppTask" concept for simple integration of further features

This commit is contained in:
Philipp Crocoll 2013-05-30 06:54:25 +02:00
parent 7c292ebd57
commit f080f13b20
10 changed files with 343 additions and 53 deletions

View File

@ -49,11 +49,8 @@ namespace keepass2android
public const String KEY_REFRESH_POS = "refresh_pos"; public const String KEY_REFRESH_POS = "refresh_pos";
public const String KEY_CLOSE_AFTER_CREATE = "close_after_create"; public const String KEY_CLOSE_AFTER_CREATE = "close_after_create";
public static void Launch(Activity act, PwEntry pw, int pos) {
Launch(act, pw, pos, false);
}
public static void Launch(Activity act, PwEntry pw, int pos, bool closeAfterCreate) { public static void Launch(Activity act, PwEntry pw, int pos, IAppTask appTask) {
Intent i; Intent i;
i = new Intent(act, typeof(EntryActivity)); i = new Intent(act, typeof(EntryActivity));
@ -61,7 +58,8 @@ namespace keepass2android
i.PutExtra(KEY_ENTRY, pw.Uuid.ToHexString()); i.PutExtra(KEY_ENTRY, pw.Uuid.ToHexString());
i.PutExtra(KEY_REFRESH_POS, pos); i.PutExtra(KEY_REFRESH_POS, pos);
i.PutExtra(KEY_CLOSE_AFTER_CREATE, closeAfterCreate);
appTask.ToIntent(i);
act.StartActivityForResult(i,0); act.StartActivityForResult(i,0);
} }
@ -71,6 +69,7 @@ namespace keepass2android
private bool mShowPassword; private bool mShowPassword;
private int mPos; private int mPos;
IAppTask mAppTask;
protected void setEntryView() { protected void setEntryView() {
@ -116,12 +115,12 @@ namespace keepass2android
PwUuid uuid = new PwUuid(MemUtil.HexStringToByteArray(i.GetStringExtra(KEY_ENTRY))); PwUuid uuid = new PwUuid(MemUtil.HexStringToByteArray(i.GetStringExtra(KEY_ENTRY)));
mPos = i.GetIntExtra(KEY_REFRESH_POS, -1); mPos = i.GetIntExtra(KEY_REFRESH_POS, -1);
bool closeAfterCreate = i.GetBooleanExtra(KEY_CLOSE_AFTER_CREATE, false); mAppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
bool closeAfterCreate = mAppTask.CloseEntryActivityAfterCreate;
mEntry = db.entries [uuid]; mEntry = db.entries [uuid];
// Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set // Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set
ActivityCompat.invalidateOptionsMenu(this); ActivityCompat.invalidateOptionsMenu(this);

View File

@ -45,11 +45,11 @@ namespace keepass2android
private const String TAG = "Group Activity:"; private const String TAG = "Group Activity:";
public static void Launch(Activity act) { public static void Launch(Activity act, IAppTask appTask) {
Launch(act, null); Launch(act, null, appTask);
} }
public static void Launch (Activity act, PwGroup g) public static void Launch (Activity act, PwGroup g, IAppTask appTask)
{ {
Intent i; Intent i;

View File

@ -40,7 +40,7 @@ namespace keepass2android
public virtual void LaunchActivityForEntry(KeePassLib.PwEntry pwEntry, int pos) public virtual void LaunchActivityForEntry(KeePassLib.PwEntry pwEntry, int pos)
{ {
EntryActivity.Launch(this, pwEntry, pos, false); EntryActivity.Launch(this, pwEntry, pos, mAppTask);
} }
public GroupBaseActivity () public GroupBaseActivity ()
{ {
@ -53,10 +53,18 @@ namespace keepass2android
} }
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
mAppTask.ToBundle(outState);
}
private ISharedPreferences prefs; private ISharedPreferences prefs;
protected PwGroup mGroup; protected PwGroup mGroup;
internal IAppTask mAppTask;
protected override void OnResume() { protected override void OnResume() {
base.OnResume(); base.OnResume();
@ -91,6 +99,8 @@ namespace keepass2android
protected override void OnCreate(Bundle savedInstanceState) { protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
mAppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
// Likely the app has been killed exit the activity // Likely the app has been killed exit the activity
if ( ! App.getDB().Loaded ) { if ( ! App.getDB().Loaded ) {
Finish(); Finish();

View File

@ -69,6 +69,7 @@ namespace keepass2android
{ {
base.OnCreate(bundle); base.OnCreate(bundle);
Android.Util.Log.Debug("DEBUG",ClassName+".OnCreate"); Android.Util.Log.Debug("DEBUG",ClassName+".OnCreate");
Android.Util.Log.Debug("DEBUG", ClassName+":apptask="+Intent.GetStringExtra("KP2A_APP_TASK_TYPE"));
} }
protected override void OnDestroy() protected override void OnDestroy()

View File

@ -87,7 +87,7 @@ namespace keepass2android
ioc.CredSaveMode = (IOCredSaveMode)i.GetIntExtra(KEY_SERVERCREDMODE, (int) IOCredSaveMode.NoSave); ioc.CredSaveMode = (IOCredSaveMode)i.GetIntExtra(KEY_SERVERCREDMODE, (int) IOCredSaveMode.NoSave);
} }
public static void Launch(Activity act, String fileName, String urlToSearchFor) { public static void Launch(Activity act, String fileName, IAppTask appTask) {
Java.IO.File dbFile = new Java.IO.File(fileName); Java.IO.File dbFile = new Java.IO.File(fileName);
if ( ! dbFile.Exists() ) { if ( ! dbFile.Exists() ) {
throw new Java.IO.FileNotFoundException(); throw new Java.IO.FileNotFoundException();
@ -96,7 +96,7 @@ namespace keepass2android
Intent i = new Intent(act, typeof(PasswordActivity)); Intent i = new Intent(act, typeof(PasswordActivity));
i.PutExtra(KEY_FILENAME, fileName); i.PutExtra(KEY_FILENAME, fileName);
i.PutExtra(FileSelectActivity.UrlToSearch_key, urlToSearchFor); appTask.ToIntent(i);
act.StartActivityForResult(i, 0); act.StartActivityForResult(i, 0);
} }
@ -108,18 +108,18 @@ namespace keepass2android
} }
public static void Launch(Activity act, IOConnectionInfo ioc, String urlToSearchFor) public static void Launch(Activity act, IOConnectionInfo ioc, IAppTask appTask)
{ {
if (ioc.IsLocalFile()) if (ioc.IsLocalFile())
{ {
Launch(act, ioc.Path, urlToSearchFor); Launch(act, ioc.Path, appTask);
return; return;
} }
Intent i = new Intent(act, typeof(PasswordActivity)); Intent i = new Intent(act, typeof(PasswordActivity));
PutIoConnectionToIntent(ioc, i); PutIoConnectionToIntent(ioc, i);
i.PutExtra(FileSelectActivity.UrlToSearch_key, urlToSearchFor); appTask.ToIntent(i);
act.StartActivityForResult(i, 0); act.StartActivityForResult(i, 0);
@ -127,13 +127,8 @@ namespace keepass2android
public void LaunchNextActivity() public void LaunchNextActivity()
{ {
mAppTask.AfterUnlockDatabase(this);
if (String.IsNullOrEmpty(mUrlToSearchFor))
GroupActivity.Launch(this);
else
{
ShareUrlResults.Launch(this, mUrlToSearchFor);
}
} }
void unloadDatabase() void unloadDatabase()
@ -284,7 +279,7 @@ namespace keepass2android
} }
internal string mUrlToSearchFor; internal IAppTask mAppTask;
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle savedInstanceState)
{ {
@ -342,7 +337,7 @@ namespace keepass2android
} }
} }
this.mUrlToSearchFor = i.GetStringExtra(FileSelectActivity.UrlToSearch_key); mAppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
SetContentView(Resource.Layout.password); SetContentView(Resource.Layout.password);
populateView(); populateView();
@ -450,6 +445,12 @@ namespace keepass2android
} }
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
mAppTask.ToBundle(outState);
}
protected override void OnResume() { protected override void OnResume() {
base.OnResume(); base.OnResume();

View File

@ -46,39 +46,38 @@ namespace keepass2android
{ {
} }
public static void Launch(Activity act, string urlToSearchFor) public static void Launch(Activity act, SearchUrlTask task)
{ {
Intent i = new Intent(act, typeof(ShareUrlResults)); Intent i = new Intent(act, typeof(ShareUrlResults));
i.PutExtra(Intent.ExtraText, urlToSearchFor); task.ToIntent(i);
act.StartActivityForResult(i, 0); act.StartActivityForResult(i, 0);
} }
private Database mDb; private Database mDb;
protected override void OnCreate(Bundle bundle) protected override void OnCreate(Bundle savedInstanceState)
{ {
base.OnCreate(bundle); base.OnCreate(savedInstanceState);
SetResult(KeePass.EXIT_CLOSE_AFTER_SEARCH); SetResult(KeePass.EXIT_CLOSE_AFTER_SEARCH);
mDb = App.getDB(); mDb = App.getDB();
String searchUrl = getSearchUrl(Intent); String searchUrl = ((SearchUrlTask)mAppTask).UrlToSearchFor;
if (!mDb.Loaded) if (!mDb.Loaded)
{ {
Intent intent = new Intent(this, typeof(FileSelectActivity)); Intent intent = new Intent(this, typeof(FileSelectActivity));
intent.PutExtra(FileSelectActivity.UrlToSearch_key, searchUrl); mAppTask.ToIntent(intent);
StartActivityForResult(intent, 0); StartActivityForResult(intent, 0);
Finish(); Finish();
} }
else if (mDb.Locked) else if (mDb.Locked)
{ {
PasswordActivity.Launch(this,mDb.mIoc, searchUrl); PasswordActivity.Launch(this,mDb.mIoc, mAppTask);
Finish(); Finish();
} }
else else
@ -88,9 +87,15 @@ namespace keepass2android
} }
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
mAppTask.ToBundle(outState);
}
public override void LaunchActivityForEntry(KeePassLib.PwEntry pwEntry, int pos) public override void LaunchActivityForEntry(KeePassLib.PwEntry pwEntry, int pos)
{ {
EntryActivity.Launch(this, pwEntry, pos, true); base.LaunchActivityForEntry(pwEntry, pos);
Finish(); Finish();
} }
@ -133,7 +138,7 @@ namespace keepass2android
} }
} }
//show results:
if (mGroup == null || (mGroup.Entries.Count() < 1)) if (mGroup == null || (mGroup.Entries.Count() < 1))
{ {
SetContentView(new GroupEmptyView(this)); SetContentView(new GroupEmptyView(this));
@ -146,6 +151,7 @@ namespace keepass2android
ListAdapter = new PwGroupListAdapter(this, mGroup); ListAdapter = new PwGroupListAdapter(this, mGroup);
//if there is exactly one match: open the entry
if (mGroup.Entries.Count() == 1) if (mGroup.Entries.Count() == 1)
{ {
LaunchActivityForEntry(mGroup.Entries.Single(),0); LaunchActivityForEntry(mGroup.Entries.Single(),0);

View File

@ -0,0 +1,268 @@
// Copyright (c) 2013 Philipp Crocoll
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
using System;
using Android.App;
using Android.Content;
using Android.OS;
using System.Collections.Generic;
namespace keepass2android
{
/// <summary>
/// Interface for data stored in an intent or bundle as extra string
/// </summary>
public interface IExtra
{
/// <summary>
/// put data to a bundle by calling one of the PutXX methods
/// </summary>
void ToBundle(Bundle b);
/// <summary>
/// Put data to an intent by calling PutExtra
/// </summary>
void ToIntent(Intent i);
}
/// <summary>
/// represents data stored in an intent or bundle as extra string
/// </summary>
public class StringExtra: IExtra
{
public string Key { get; set; }
public string Value{ get; set; }
#region IExtra implementation
public void ToBundle(Bundle b)
{
b.PutString(Key, Value);
}
public void ToIntent(Intent i)
{
i.PutExtra(Key, Value);
}
#endregion
}
/// <summary>
/// interface for "tasks": this are things the user wants to do and which require several activities
/// </summary>
public interface IAppTask
{
/// <summary>
/// Loads the parameters of the task from the given bundle
/// </summary>
void Setup(Bundle b);
/// <summary>
/// Returns the parameters of the task for storage in a bundle or intent
/// </summary>
/// <value>The extras.</value>
IEnumerable<IExtra> Extras { get;}
void AfterUnlockDatabase(PasswordActivity act);
bool CloseEntryActivityAfterCreate
{
get;
}
}
/// <summary>
/// Implementation of IAppTask for "no task currently active" (Null pattern)
/// </summary>
public class NullTask: IAppTask
{
public void Setup(Bundle b)
{
}
public IEnumerable<IExtra> Extras
{
get
{
yield break;
}
}
public void AfterUnlockDatabase(PasswordActivity act)
{
GroupActivity.Launch(act, this);
}
public bool CloseEntryActivityAfterCreate
{
get { return false;}
}
}
/// <summary>
/// User is about to search an entry for a given URL
/// </summary>
public class SearchUrlTask: IAppTask
{
public const String UrlToSearch_key = "UrlToSearch";
public string UrlToSearchFor
{
get;
set;
}
public void Setup(Bundle b)
{
UrlToSearchFor = b.GetString(UrlToSearch_key);
}
public IEnumerable<IExtra> Extras
{
get
{
yield return new StringExtra() { Key=UrlToSearch_key, Value = UrlToSearchFor };
}
}
public void AfterUnlockDatabase(PasswordActivity act)
{
ShareUrlResults.Launch(act, this);
}
public bool CloseEntryActivityAfterCreate
{
get { return true;}
}
}
/// <summary>
/// User is about to select an entry for use in another app
/// </summary>
public class SelectEntryTask: IAppTask
{
public void Setup(Bundle b)
{
}
public IEnumerable<IExtra> Extras
{
get
{
yield break;
}
}
public void AfterUnlockDatabase(PasswordActivity act)
{
GroupActivity.Launch(act, this);
}
public bool CloseEntryActivityAfterCreate
{
//keypoint here: close the app after selecting the entry
get { return true;}
}
}
/// <summary>
///
/// </summary>
public static class AppTask
{
public const String AppTask_key = "KP2A_APPTASK";
/// <summary>
/// Should be used in OnCreate to (re)create a task
/// if savedInstanceState is not null, the task is recreated from there. Otherwise it's taken from the intent.
/// </summary>
public static IAppTask GetTaskInOnCreate(Bundle savedInstanceState, Intent intent)
{
if (savedInstanceState != null)
{
return AppTask.CreateFromBundle(savedInstanceState);
}
else
{
return AppTask.CreateFromIntent(intent);
}
}
public static IAppTask CreateFromIntent(Intent i)
{
return CreateFromBundle(i.Extras);
}
public static IAppTask CreateFromBundle(Bundle b)
{
if (b == null)
return new NullTask();
string taskType = b.GetString("KP2A_APP_TASK_TYPE");
if (string.IsNullOrEmpty(taskType))
return new NullTask();
Type[] types = {typeof(SearchUrlTask), typeof(NullTask)};
foreach (Type type in types)
{
if (taskType == type.Name)
{
IAppTask task = (IAppTask)Activator.CreateInstance(type);
task.Setup(b);
return task;
}
}
return new NullTask();
}
/// <summary>
/// Adds the extras of the task to the intent
/// </summary>
public static void ToIntent(this IAppTask task, Intent intent)
{
AppTask.GetTypeExtra(task.GetType()).ToIntent(intent);
foreach (IExtra extra in task.Extras)
{
extra.ToIntent(intent);
}
}
/// <summary>
/// Adds the extras of the task to the bundle
/// </summary>
public static void ToBundle(this IAppTask task, Bundle bundle)
{
AppTask.GetTypeExtra(task.GetType()).ToBundle(bundle);
foreach (IExtra extra in task.Extras)
{
extra.ToBundle(bundle);
}
}
/// <summary>
/// Returns an IExtra which must be part of the Extras of a task to describe the type
/// </summary>
static IExtra GetTypeExtra(Type type)
{
return new StringExtra() { Key="KP2A_APP_TASK_TYPE", Value=type.Name};
}
}
}

View File

@ -56,17 +56,16 @@ namespace keepass2android
private const int CMENU_CLEAR = Menu.First; private const int CMENU_CLEAR = Menu.First;
public const String UrlToSearch_key = "UrlToSearch";
const String BundleKey_UrlToSearchFor = "UrlToSearch";
const string BundleKey_RecentMode = "RecentMode"; const string BundleKey_RecentMode = "RecentMode";
private FileDbHelper mDbHelper; private FileDbHelper mDbHelper;
private String mUrlToSearch;
private bool recentMode = false; private bool recentMode = false;
view.FileSelectButtons fileSelectButtons; view.FileSelectButtons fileSelectButtons;
bool createdWithActivityResult = false; bool createdWithActivityResult = false;
internal IAppTask mAppTask;
IOConnectionInfo loadIoc(string defaultFileName) IOConnectionInfo loadIoc(string defaultFileName)
{ {
return mDbHelper.cursorToIoc(mDbHelper.fetchFileByName(defaultFileName)); return mDbHelper.cursorToIoc(mDbHelper.fetchFileByName(defaultFileName));
@ -205,11 +204,16 @@ namespace keepass2android
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
Android.Util.Log.Debug("DEBUG", "FileSelect.OnCreate"); Android.Util.Log.Debug("DEBUG", "FileSelect.OnCreate");
Android.Util.Log.Debug("DEBUG", "FileSelect:apptask="+Intent.GetStringExtra("KP2A_APP_TASK_TYPE"));
if (Intent.Action == Intent.ActionSend) if (Intent.Action == Intent.ActionSend)
mUrlToSearch = Intent.GetStringExtra(Intent.ExtraText); {
mAppTask = new SearchUrlTask() { UrlToSearchFor = Intent.GetStringExtra(Intent.ExtraText) };
}
else else
mUrlToSearch = Intent.GetStringExtra(UrlToSearch_key); {
mAppTask = AppTask.CreateFromIntent(Intent);
}
mDbHelper = App.fileDbHelper; mDbHelper = App.fileDbHelper;
@ -283,7 +287,7 @@ namespace keepass2android
if (savedInstanceState != null) if (savedInstanceState != null)
{ {
mUrlToSearch = savedInstanceState.GetString(BundleKey_UrlToSearchFor, null); mAppTask = AppTask.CreateFromBundle(savedInstanceState);
recentMode = savedInstanceState.GetBoolean(BundleKey_RecentMode, recentMode); recentMode = savedInstanceState.GetBoolean(BundleKey_RecentMode, recentMode);
@ -295,18 +299,18 @@ namespace keepass2android
protected override void OnSaveInstanceState(Bundle outState) protected override void OnSaveInstanceState(Bundle outState)
{ {
base.OnSaveInstanceState(outState); base.OnSaveInstanceState(outState);
outState.PutString(BundleKey_UrlToSearchFor, mUrlToSearch); mAppTask.ToBundle(outState);
outState.PutBoolean(BundleKey_RecentMode, recentMode); outState.PutBoolean(BundleKey_RecentMode, recentMode);
} }
private class LaunchGroupActivity : FileOnFinish { private class LaunchGroupActivity : FileOnFinish {
FileSelectActivity activty; FileSelectActivity activity;
private IOConnectionInfo mIoc; private IOConnectionInfo mIoc;
public LaunchGroupActivity(IOConnectionInfo ioc, FileSelectActivity activty): base(null) { public LaunchGroupActivity(IOConnectionInfo ioc, FileSelectActivity activity): base(null) {
this.activty = activty; this.activity = activity;
mIoc = ioc; mIoc = ioc;
} }
@ -318,7 +322,7 @@ namespace keepass2android
//TODO: getFilename always returns "" -> bug? //TODO: getFilename always returns "" -> bug?
dbHelper.createFile(mIoc, getFilename()); dbHelper.createFile(mIoc, getFilename());
GroupActivity.Launch(activty); GroupActivity.Launch(activity, activity.mAppTask);
} else { } else {
IOConnection.DeleteFile(mIoc); IOConnection.DeleteFile(mIoc);
@ -380,7 +384,7 @@ namespace keepass2android
ioc.UserName = username; ioc.UserName = username;
ioc.Password = password; ioc.Password = password;
ioc.CredSaveMode = (IOCredSaveMode)credentialRememberMode; ioc.CredSaveMode = (IOCredSaveMode)credentialRememberMode;
PasswordActivity.Launch(this, ioc, mUrlToSearch); PasswordActivity.Launch(this, ioc, mAppTask);
})); }));
builder.SetView(LayoutInflater.Inflate(Resource.Layout.url_credentials, null)); builder.SetView(LayoutInflater.Inflate(Resource.Layout.url_credentials, null));
builder.SetNeutralButton(GetString(Android.Resource.String.Cancel), builder.SetNeutralButton(GetString(Android.Resource.String.Cancel),
@ -395,7 +399,7 @@ namespace keepass2android
{ {
try try
{ {
PasswordActivity.Launch(this, ioc, mUrlToSearch); PasswordActivity.Launch(this, ioc, mAppTask);
} catch (Java.IO.FileNotFoundException) } catch (Java.IO.FileNotFoundException)
{ {
Toast.MakeText(this, Resource.String.FileNotFound, ToastLength.Long).Show(); Toast.MakeText(this, Resource.String.FileNotFound, ToastLength.Long).Show();
@ -482,7 +486,7 @@ namespace keepass2android
{ {
if ((Intent.Action == Intent.ActionSend) && (App.getDB().Loaded)) if ((Intent.Action == Intent.ActionSend) && (App.getDB().Loaded))
{ {
PasswordActivity.Launch(this, App.getDB().mIoc , mUrlToSearch); PasswordActivity.Launch(this, App.getDB().mIoc , mAppTask);
} else } else
{ {
@ -498,7 +502,7 @@ namespace keepass2android
{ {
try try
{ {
PasswordActivity.Launch(this, loadIoc(defaultFileName), mUrlToSearch); PasswordActivity.Launch(this, loadIoc(defaultFileName), mAppTask);
} catch (Exception e) } catch (Exception e)
{ {
Toast.MakeText(this, e.Message, ToastLength.Long); Toast.MakeText(this, e.Message, ToastLength.Long);

View File

@ -160,6 +160,7 @@
<Compile Include="views\FileSelectButtons.cs" /> <Compile Include="views\FileSelectButtons.cs" />
<Compile Include="EntryEditActivityState.cs" /> <Compile Include="EntryEditActivityState.cs" />
<Compile Include="AttachmentContentProvider.cs" /> <Compile Include="AttachmentContentProvider.cs" />
<Compile Include="app\AppTask.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Resources\AboutResources.txt" /> <None Include="Resources\AboutResources.txt" />

View File

@ -89,7 +89,7 @@ namespace keepass2android.view
} }
private void launchGroup() { private void launchGroup() {
GroupActivity.Launch(mAct, mPw); GroupActivity.Launch(mAct, mPw, mAct.mAppTask);
mAct.OverridePendingTransition(Resource.Animation.anim_enter, Resource.Animation.anim_leave); mAct.OverridePendingTransition(Resource.Animation.anim_enter, Resource.Animation.anim_leave);
} }