diff --git a/src/keepass2android/EntryActivity.cs b/src/keepass2android/EntryActivity.cs index cfd06273..c325f9d3 100644 --- a/src/keepass2android/EntryActivity.cs +++ b/src/keepass2android/EntryActivity.cs @@ -49,11 +49,8 @@ namespace keepass2android public const String KEY_REFRESH_POS = "refresh_pos"; 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; i = new Intent(act, typeof(EntryActivity)); @@ -61,7 +58,8 @@ namespace keepass2android i.PutExtra(KEY_ENTRY, pw.Uuid.ToHexString()); i.PutExtra(KEY_REFRESH_POS, pos); - i.PutExtra(KEY_CLOSE_AFTER_CREATE, closeAfterCreate); + + appTask.ToIntent(i); act.StartActivityForResult(i,0); } @@ -71,6 +69,7 @@ namespace keepass2android private bool mShowPassword; private int mPos; + IAppTask mAppTask; protected void setEntryView() { @@ -116,12 +115,12 @@ namespace keepass2android PwUuid uuid = new PwUuid(MemUtil.HexStringToByteArray(i.GetStringExtra(KEY_ENTRY))); 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]; - - // Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set ActivityCompat.invalidateOptionsMenu(this); diff --git a/src/keepass2android/GroupActivity.cs b/src/keepass2android/GroupActivity.cs index 6a096ae5..3b3b3eca 100644 --- a/src/keepass2android/GroupActivity.cs +++ b/src/keepass2android/GroupActivity.cs @@ -45,11 +45,11 @@ namespace keepass2android private const String TAG = "Group Activity:"; - public static void Launch(Activity act) { - Launch(act, null); + public static void Launch(Activity act, IAppTask appTask) { + Launch(act, null, appTask); } - public static void Launch (Activity act, PwGroup g) + public static void Launch (Activity act, PwGroup g, IAppTask appTask) { Intent i; diff --git a/src/keepass2android/GroupBaseActivity.cs b/src/keepass2android/GroupBaseActivity.cs index 56c057c3..84bbfb85 100644 --- a/src/keepass2android/GroupBaseActivity.cs +++ b/src/keepass2android/GroupBaseActivity.cs @@ -40,7 +40,7 @@ namespace keepass2android public virtual void LaunchActivityForEntry(KeePassLib.PwEntry pwEntry, int pos) { - EntryActivity.Launch(this, pwEntry, pos, false); + EntryActivity.Launch(this, pwEntry, pos, mAppTask); } public GroupBaseActivity () { @@ -52,10 +52,18 @@ namespace keepass2android { } + + protected override void OnSaveInstanceState(Bundle outState) + { + base.OnSaveInstanceState(outState); + mAppTask.ToBundle(outState); + } private ISharedPreferences prefs; protected PwGroup mGroup; + + internal IAppTask mAppTask; protected override void OnResume() { base.OnResume(); @@ -90,6 +98,8 @@ namespace keepass2android protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); + + mAppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent); // Likely the app has been killed exit the activity if ( ! App.getDB().Loaded ) { diff --git a/src/keepass2android/LifecycleDebugActivity.cs b/src/keepass2android/LifecycleDebugActivity.cs index c6609bc5..410a31c0 100644 --- a/src/keepass2android/LifecycleDebugActivity.cs +++ b/src/keepass2android/LifecycleDebugActivity.cs @@ -69,6 +69,7 @@ namespace keepass2android { base.OnCreate(bundle); Android.Util.Log.Debug("DEBUG",ClassName+".OnCreate"); + Android.Util.Log.Debug("DEBUG", ClassName+":apptask="+Intent.GetStringExtra("KP2A_APP_TASK_TYPE")); } protected override void OnDestroy() diff --git a/src/keepass2android/PasswordActivity.cs b/src/keepass2android/PasswordActivity.cs index a14f2508..2ade556a 100644 --- a/src/keepass2android/PasswordActivity.cs +++ b/src/keepass2android/PasswordActivity.cs @@ -87,7 +87,7 @@ namespace keepass2android 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); if ( ! dbFile.Exists() ) { throw new Java.IO.FileNotFoundException(); @@ -96,7 +96,7 @@ namespace keepass2android Intent i = new Intent(act, typeof(PasswordActivity)); i.PutExtra(KEY_FILENAME, fileName); - i.PutExtra(FileSelectActivity.UrlToSearch_key, urlToSearchFor); + appTask.ToIntent(i); 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()) { - Launch(act, ioc.Path, urlToSearchFor); + Launch(act, ioc.Path, appTask); return; } Intent i = new Intent(act, typeof(PasswordActivity)); PutIoConnectionToIntent(ioc, i); - i.PutExtra(FileSelectActivity.UrlToSearch_key, urlToSearchFor); + appTask.ToIntent(i); act.StartActivityForResult(i, 0); @@ -127,13 +127,8 @@ namespace keepass2android public void LaunchNextActivity() { + mAppTask.AfterUnlockDatabase(this); - if (String.IsNullOrEmpty(mUrlToSearchFor)) - GroupActivity.Launch(this); - else - { - ShareUrlResults.Launch(this, mUrlToSearchFor); - } } void unloadDatabase() @@ -284,7 +279,7 @@ namespace keepass2android } - internal string mUrlToSearchFor; + internal IAppTask mAppTask; 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); populateView(); @@ -449,6 +444,12 @@ namespace keepass2android } + + protected override void OnSaveInstanceState(Bundle outState) + { + base.OnSaveInstanceState(outState); + mAppTask.ToBundle(outState); + } protected override void OnResume() { base.OnResume(); diff --git a/src/keepass2android/ShareUrlResults.cs b/src/keepass2android/ShareUrlResults.cs index e3baadd0..f066562c 100644 --- a/src/keepass2android/ShareUrlResults.cs +++ b/src/keepass2android/ShareUrlResults.cs @@ -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)); - i.PutExtra(Intent.ExtraText, urlToSearchFor); + task.ToIntent(i); act.StartActivityForResult(i, 0); - } 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); - + mDb = App.getDB(); - String searchUrl = getSearchUrl(Intent); + String searchUrl = ((SearchUrlTask)mAppTask).UrlToSearchFor; if (!mDb.Loaded) { Intent intent = new Intent(this, typeof(FileSelectActivity)); - intent.PutExtra(FileSelectActivity.UrlToSearch_key, searchUrl); + mAppTask.ToIntent(intent); StartActivityForResult(intent, 0); Finish(); } else if (mDb.Locked) { - PasswordActivity.Launch(this,mDb.mIoc, searchUrl); + PasswordActivity.Launch(this,mDb.mIoc, mAppTask); Finish(); } 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) { - EntryActivity.Launch(this, pwEntry, pos, true); + base.LaunchActivityForEntry(pwEntry, pos); Finish(); } @@ -133,7 +138,7 @@ namespace keepass2android } } - + //show results: if (mGroup == null || (mGroup.Entries.Count() < 1)) { SetContentView(new GroupEmptyView(this)); @@ -146,6 +151,7 @@ namespace keepass2android ListAdapter = new PwGroupListAdapter(this, mGroup); + //if there is exactly one match: open the entry if (mGroup.Entries.Count() == 1) { LaunchActivityForEntry(mGroup.Entries.Single(),0); diff --git a/src/keepass2android/app/AppTask.cs b/src/keepass2android/app/AppTask.cs new file mode 100644 index 00000000..28f650bd --- /dev/null +++ b/src/keepass2android/app/AppTask.cs @@ -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 +{ + /// + /// Interface for data stored in an intent or bundle as extra string + /// + public interface IExtra + { + /// + /// put data to a bundle by calling one of the PutXX methods + /// + void ToBundle(Bundle b); + + /// + /// Put data to an intent by calling PutExtra + /// + void ToIntent(Intent i); + } + + /// + /// represents data stored in an intent or bundle as extra string + /// + 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 + } + + /// + /// interface for "tasks": this are things the user wants to do and which require several activities + /// + public interface IAppTask + { + /// + /// Loads the parameters of the task from the given bundle + /// + void Setup(Bundle b); + + /// + /// Returns the parameters of the task for storage in a bundle or intent + /// + /// The extras. + IEnumerable Extras { get;} + + void AfterUnlockDatabase(PasswordActivity act); + + bool CloseEntryActivityAfterCreate + { + get; + } + } + + /// + /// Implementation of IAppTask for "no task currently active" (Null pattern) + /// + public class NullTask: IAppTask + { + + public void Setup(Bundle b) + { + } + + public IEnumerable Extras + { + get + { + yield break; + } + } + + + public void AfterUnlockDatabase(PasswordActivity act) + { + GroupActivity.Launch(act, this); + } + public bool CloseEntryActivityAfterCreate + { + get { return false;} + } + } + + /// + /// User is about to search an entry for a given URL + /// + 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 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;} + } + } + + + /// + /// User is about to select an entry for use in another app + /// + public class SelectEntryTask: IAppTask + { + public void Setup(Bundle b) + { + } + public IEnumerable 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;} + } + } + + /// + /// + /// + public static class AppTask + { + public const String AppTask_key = "KP2A_APPTASK"; + + /// + /// 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. + /// + 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(); + } + + /// + /// Adds the extras of the task to the intent + /// + public static void ToIntent(this IAppTask task, Intent intent) + { + AppTask.GetTypeExtra(task.GetType()).ToIntent(intent); + + foreach (IExtra extra in task.Extras) + { + extra.ToIntent(intent); + } + } + + /// + /// Adds the extras of the task to the bundle + /// + public static void ToBundle(this IAppTask task, Bundle bundle) + { + AppTask.GetTypeExtra(task.GetType()).ToBundle(bundle); + + foreach (IExtra extra in task.Extras) + { + extra.ToBundle(bundle); + } + + } + + /// + /// Returns an IExtra which must be part of the Extras of a task to describe the type + /// + static IExtra GetTypeExtra(Type type) + { + return new StringExtra() { Key="KP2A_APP_TASK_TYPE", Value=type.Name}; + } + + } +} + diff --git a/src/keepass2android/fileselect/FileSelectActivity.cs b/src/keepass2android/fileselect/FileSelectActivity.cs index ee44e4e3..39ba3d67 100644 --- a/src/keepass2android/fileselect/FileSelectActivity.cs +++ b/src/keepass2android/fileselect/FileSelectActivity.cs @@ -56,17 +56,16 @@ namespace keepass2android private const int CMENU_CLEAR = Menu.First; - public const String UrlToSearch_key = "UrlToSearch"; - const String BundleKey_UrlToSearchFor = "UrlToSearch"; const string BundleKey_RecentMode = "RecentMode"; private FileDbHelper mDbHelper; - private String mUrlToSearch; - + private bool recentMode = false; view.FileSelectButtons fileSelectButtons; bool createdWithActivityResult = false; + internal IAppTask mAppTask; + IOConnectionInfo loadIoc(string defaultFileName) { return mDbHelper.cursorToIoc(mDbHelper.fetchFileByName(defaultFileName)); @@ -205,11 +204,16 @@ namespace keepass2android base.OnCreate(savedInstanceState); 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) - mUrlToSearch = Intent.GetStringExtra(Intent.ExtraText); + { + mAppTask = new SearchUrlTask() { UrlToSearchFor = Intent.GetStringExtra(Intent.ExtraText) }; + } else - mUrlToSearch = Intent.GetStringExtra(UrlToSearch_key); + { + mAppTask = AppTask.CreateFromIntent(Intent); + } mDbHelper = App.fileDbHelper; @@ -283,7 +287,7 @@ namespace keepass2android if (savedInstanceState != null) { - mUrlToSearch = savedInstanceState.GetString(BundleKey_UrlToSearchFor, null); + mAppTask = AppTask.CreateFromBundle(savedInstanceState); recentMode = savedInstanceState.GetBoolean(BundleKey_RecentMode, recentMode); @@ -295,18 +299,18 @@ namespace keepass2android protected override void OnSaveInstanceState(Bundle outState) { base.OnSaveInstanceState(outState); - outState.PutString(BundleKey_UrlToSearchFor, mUrlToSearch); + mAppTask.ToBundle(outState); outState.PutBoolean(BundleKey_RecentMode, recentMode); } private class LaunchGroupActivity : FileOnFinish { - FileSelectActivity activty; + FileSelectActivity activity; 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; } @@ -318,7 +322,7 @@ namespace keepass2android //TODO: getFilename always returns "" -> bug? dbHelper.createFile(mIoc, getFilename()); - GroupActivity.Launch(activty); + GroupActivity.Launch(activity, activity.mAppTask); } else { IOConnection.DeleteFile(mIoc); @@ -380,7 +384,7 @@ namespace keepass2android ioc.UserName = username; ioc.Password = password; ioc.CredSaveMode = (IOCredSaveMode)credentialRememberMode; - PasswordActivity.Launch(this, ioc, mUrlToSearch); + PasswordActivity.Launch(this, ioc, mAppTask); })); builder.SetView(LayoutInflater.Inflate(Resource.Layout.url_credentials, null)); builder.SetNeutralButton(GetString(Android.Resource.String.Cancel), @@ -395,7 +399,7 @@ namespace keepass2android { try { - PasswordActivity.Launch(this, ioc, mUrlToSearch); + PasswordActivity.Launch(this, ioc, mAppTask); } catch (Java.IO.FileNotFoundException) { Toast.MakeText(this, Resource.String.FileNotFound, ToastLength.Long).Show(); @@ -482,7 +486,7 @@ namespace keepass2android { if ((Intent.Action == Intent.ActionSend) && (App.getDB().Loaded)) { - PasswordActivity.Launch(this, App.getDB().mIoc , mUrlToSearch); + PasswordActivity.Launch(this, App.getDB().mIoc , mAppTask); } else { @@ -498,7 +502,7 @@ namespace keepass2android { try { - PasswordActivity.Launch(this, loadIoc(defaultFileName), mUrlToSearch); + PasswordActivity.Launch(this, loadIoc(defaultFileName), mAppTask); } catch (Exception e) { Toast.MakeText(this, e.Message, ToastLength.Long); diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index 29d87522..b14a19be 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -160,6 +160,7 @@ + diff --git a/src/keepass2android/views/PwGroupView.cs b/src/keepass2android/views/PwGroupView.cs index 5be3d751..8d49d1b4 100644 --- a/src/keepass2android/views/PwGroupView.cs +++ b/src/keepass2android/views/PwGroupView.cs @@ -89,7 +89,7 @@ namespace keepass2android.view } private void launchGroup() { - GroupActivity.Launch(mAct, mPw); + GroupActivity.Launch(mAct, mPw, mAct.mAppTask); mAct.OverridePendingTransition(Resource.Animation.anim_enter, Resource.Animation.anim_leave); }