2013-02-23 11:43:42 -05:00
|
|
|
/*
|
|
|
|
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
|
|
|
|
|
|
|
|
Keepass2Android is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Keepass2Android is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
using Android.App;
|
|
|
|
using Android.Content;
|
|
|
|
using Android.OS;
|
|
|
|
using Android.Runtime;
|
|
|
|
using Android.Views;
|
|
|
|
using Android.Widget;
|
|
|
|
using KeePassLib;
|
|
|
|
using Android.Preferences;
|
|
|
|
using keepass2android.view;
|
2013-04-12 23:28:15 -04:00
|
|
|
using Android.Graphics.Drawables;
|
2013-02-23 11:43:42 -05:00
|
|
|
|
|
|
|
namespace keepass2android
|
|
|
|
{
|
|
|
|
|
|
|
|
public abstract class GroupBaseActivity : LockCloseListActivity {
|
|
|
|
public const String KEY_ENTRY = "entry";
|
|
|
|
public const String KEY_MODE = "mode";
|
|
|
|
|
|
|
|
public virtual void LaunchActivityForEntry(KeePassLib.PwEntry pwEntry, int pos)
|
|
|
|
{
|
2013-05-30 00:54:25 -04:00
|
|
|
EntryActivity.Launch(this, pwEntry, pos, mAppTask);
|
2013-02-23 11:43:42 -05:00
|
|
|
}
|
|
|
|
public GroupBaseActivity ()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public GroupBaseActivity (IntPtr javaReference, JniHandleOwnership transfer)
|
|
|
|
: base(javaReference, transfer)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
2013-05-30 00:54:25 -04:00
|
|
|
|
|
|
|
protected override void OnSaveInstanceState(Bundle outState)
|
|
|
|
{
|
|
|
|
base.OnSaveInstanceState(outState);
|
|
|
|
mAppTask.ToBundle(outState);
|
|
|
|
}
|
2013-02-23 11:43:42 -05:00
|
|
|
|
|
|
|
private ISharedPreferences prefs;
|
|
|
|
|
|
|
|
protected PwGroup mGroup;
|
2013-05-30 00:54:25 -04:00
|
|
|
|
|
|
|
internal IAppTask mAppTask;
|
2013-02-23 11:43:42 -05:00
|
|
|
|
|
|
|
protected override void OnResume() {
|
|
|
|
base.OnResume();
|
|
|
|
|
|
|
|
refreshIfDirty();
|
|
|
|
}
|
|
|
|
|
|
|
|
public override bool OnSearchRequested()
|
|
|
|
{
|
|
|
|
StartActivityForResult(typeof(SearchActivity), 0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void refreshIfDirty() {
|
|
|
|
Database db = App.getDB();
|
|
|
|
if ( db.dirty.Contains(mGroup) ) {
|
|
|
|
db.dirty.Remove(mGroup);
|
|
|
|
BaseAdapter adapter = (BaseAdapter) ListAdapter;
|
|
|
|
adapter.NotifyDataSetChanged();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void OnListItemClick(ListView l, View v, int position, long id) {
|
|
|
|
base.OnListItemClick(l, v, position, id);
|
|
|
|
|
|
|
|
Android.Widget.IListAdapter adapt = ListAdapter;
|
|
|
|
ClickView cv = (ClickView) adapt.GetView(position, null, null);
|
|
|
|
cv.OnClick();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void OnCreate(Bundle savedInstanceState) {
|
|
|
|
base.OnCreate(savedInstanceState);
|
2013-05-30 00:54:25 -04:00
|
|
|
|
|
|
|
mAppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
|
2013-02-23 11:43:42 -05:00
|
|
|
|
|
|
|
// Likely the app has been killed exit the activity
|
|
|
|
if ( ! App.getDB().Loaded ) {
|
|
|
|
Finish();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
prefs = PreferenceManager.GetDefaultSharedPreferences(this);
|
|
|
|
|
|
|
|
SetContentView(new GroupViewOnlyView(this));
|
|
|
|
SetResult(KeePass.EXIT_NORMAL);
|
|
|
|
|
|
|
|
styleScrollBars();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void styleScrollBars() {
|
|
|
|
ListView lv = ListView;
|
|
|
|
lv.ScrollBarStyle =ScrollbarStyles.InsideInset;
|
|
|
|
lv.TextFilterEnabled = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected void setGroupTitle()
|
|
|
|
{
|
2013-04-12 23:28:15 -04:00
|
|
|
String name = mGroup.Name;
|
|
|
|
String titleText;
|
|
|
|
bool clickable = (mGroup != null) && (mGroup.IsVirtual == false) && (mGroup.ParentGroup != null);
|
|
|
|
if (!String.IsNullOrEmpty(name))
|
|
|
|
{
|
|
|
|
titleText = name;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
titleText = GetText(Resource.String.root);
|
|
|
|
}
|
|
|
|
|
|
|
|
//see if the button for SDK Version < 11 is there
|
2013-02-23 11:43:42 -05:00
|
|
|
Button tv = (Button)FindViewById(Resource.Id.group_name);
|
2013-04-12 23:28:15 -04:00
|
|
|
if (tv != null)
|
2013-02-23 11:43:42 -05:00
|
|
|
{
|
2013-04-12 23:28:15 -04:00
|
|
|
if (mGroup != null)
|
|
|
|
{
|
|
|
|
tv.Text = titleText;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (clickable)
|
2013-02-23 11:43:42 -05:00
|
|
|
{
|
2013-04-12 23:28:15 -04:00
|
|
|
tv.Click += (object sender, EventArgs e) =>
|
|
|
|
{
|
|
|
|
Finish();
|
|
|
|
};
|
2013-02-23 11:43:42 -05:00
|
|
|
} else
|
|
|
|
{
|
2013-04-12 23:28:15 -04:00
|
|
|
tv.SetCompoundDrawables(null, null, null, null);
|
|
|
|
tv.Clickable = false;
|
2013-02-23 11:43:42 -05:00
|
|
|
}
|
|
|
|
}
|
2013-04-12 23:28:15 -04:00
|
|
|
//ICS?
|
2013-04-28 16:46:31 -04:00
|
|
|
if (Util.HasActionBar(this))
|
2013-02-23 11:43:42 -05:00
|
|
|
{
|
2013-04-12 23:28:15 -04:00
|
|
|
ActionBar.Title = titleText;
|
|
|
|
if (clickable)
|
|
|
|
ActionBar.SetDisplayHomeAsUpEnabled(true);
|
2013-02-23 11:43:42 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected void setGroupIcon() {
|
|
|
|
if (mGroup != null) {
|
2013-04-12 23:28:15 -04:00
|
|
|
Drawable drawable = App.getDB().drawFactory.getIconDrawable(Resources, App.getDB().pm, mGroup.IconId, mGroup.CustomIconUuid);
|
2013-02-23 11:43:42 -05:00
|
|
|
ImageView iv = (ImageView) FindViewById(Resource.Id.icon);
|
2013-04-12 23:28:15 -04:00
|
|
|
if (iv != null)
|
|
|
|
iv.SetImageDrawable(drawable);
|
2013-04-28 16:46:31 -04:00
|
|
|
if (Util.HasActionBar(this))
|
2013-04-12 23:28:15 -04:00
|
|
|
ActionBar.SetIcon(drawable);
|
2013-02-23 11:43:42 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public override bool OnCreateOptionsMenu(IMenu menu) {
|
|
|
|
base.OnCreateOptionsMenu(menu);
|
|
|
|
|
|
|
|
MenuInflater inflater = MenuInflater;
|
|
|
|
inflater.Inflate(Resource.Menu.group, menu);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setSortMenuText(IMenu menu) {
|
|
|
|
bool sortByName = prefs.GetBoolean(GetString(Resource.String.sort_key), Resources.GetBoolean(Resource.Boolean.sort_default));
|
|
|
|
|
|
|
|
int resId;
|
|
|
|
if ( sortByName ) {
|
|
|
|
resId = Resource.String.sort_db;
|
|
|
|
} else {
|
|
|
|
resId = Resource.String.sort_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.FindItem(Resource.Id.menu_sort).SetTitle(resId);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public override bool OnPrepareOptionsMenu(IMenu menu) {
|
|
|
|
if ( ! base.OnPrepareOptionsMenu(menu) ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
setSortMenuText(menu);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override bool OnOptionsItemSelected(IMenuItem item) {
|
|
|
|
switch ( item.ItemId ) {
|
2013-04-06 14:26:16 -04:00
|
|
|
case Resource.Id.menu_donate:
|
2013-02-23 11:43:42 -05:00
|
|
|
try {
|
2013-04-09 16:59:33 -04:00
|
|
|
Util.gotoDonateUrl(this);
|
2013-02-23 11:43:42 -05:00
|
|
|
} catch (ActivityNotFoundException) {
|
|
|
|
Toast.MakeText(this, Resource.String.error_failed_to_launch_link, ToastLength.Long).Show();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-04-06 14:26:16 -04:00
|
|
|
return true;
|
2013-02-23 11:43:42 -05:00
|
|
|
case Resource.Id.menu_lock:
|
|
|
|
App.setShutdown();
|
|
|
|
SetResult(KeePass.EXIT_LOCK);
|
|
|
|
Finish();
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case Resource.Id.menu_search:
|
|
|
|
OnSearchRequested();
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case Resource.Id.menu_app_settings:
|
|
|
|
AppSettingsActivity.Launch(this);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case Resource.Id.menu_change_master_key:
|
|
|
|
setPassword();
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case Resource.Id.menu_sort:
|
|
|
|
toggleSort();
|
|
|
|
return true;
|
2013-03-23 16:19:37 -04:00
|
|
|
case Resource.Id.menu_rate:
|
|
|
|
try {
|
|
|
|
Util.gotoMarket(this);
|
|
|
|
} catch (ActivityNotFoundException) {
|
|
|
|
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case Resource.Id.menu_suggest_improvements:
|
|
|
|
try {
|
|
|
|
Util.gotoUrl(this, Resource.String.SuggestionsURL);
|
|
|
|
} catch (ActivityNotFoundException) {
|
|
|
|
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case Resource.Id.menu_translate:
|
|
|
|
try {
|
|
|
|
Util.gotoUrl(this, Resource.String.TranslationURL);
|
|
|
|
} catch (ActivityNotFoundException) {
|
|
|
|
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
|
|
|
}
|
|
|
|
return true;
|
2013-04-12 23:28:15 -04:00
|
|
|
case Android.Resource.Id.Home:
|
|
|
|
//Currently the action bar only displays the home button when we come from a previous activity.
|
|
|
|
//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();
|
2013-05-17 00:53:58 -04:00
|
|
|
OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
|
|
|
|
|
2013-04-12 23:28:15 -04:00
|
|
|
return true;
|
2013-02-23 11:43:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return base.OnOptionsItemSelected(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void toggleSort() {
|
|
|
|
// Toggle setting
|
|
|
|
String sortKey = GetString(Resource.String.sort_key);
|
|
|
|
bool sortByName = prefs.GetBoolean(sortKey, Resources.GetBoolean(Resource.Boolean.sort_default));
|
|
|
|
ISharedPreferencesEditor editor = prefs.Edit();
|
|
|
|
editor.PutBoolean(sortKey, ! sortByName);
|
|
|
|
EditorCompat.apply(editor);
|
|
|
|
|
|
|
|
// Refresh menu titles
|
|
|
|
ActivityCompat.invalidateOptionsMenu(this);
|
|
|
|
|
|
|
|
// Mark all groups as dirty now to refresh them on load
|
|
|
|
Database db = App.getDB();
|
|
|
|
db.markAllGroupsAsDirty();
|
|
|
|
// We'll manually refresh this group so we can remove it
|
|
|
|
db.dirty.Remove(mGroup);
|
|
|
|
|
|
|
|
// Tell the adapter to refresh it's list
|
|
|
|
BaseAdapter adapter = (BaseAdapter) ListAdapter;
|
|
|
|
adapter.NotifyDataSetChanged();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setPassword() {
|
|
|
|
SetPasswordDialog dialog = new SetPasswordDialog(this);
|
|
|
|
dialog.Show();
|
|
|
|
}
|
|
|
|
|
|
|
|
public class RefreshTask : OnFinish {
|
|
|
|
GroupBaseActivity act;
|
|
|
|
public RefreshTask(Handler handler, GroupBaseActivity act):base(handler) {
|
|
|
|
this.act = act;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void run() {
|
|
|
|
if ( mSuccess) {
|
|
|
|
act.refreshIfDirty();
|
|
|
|
} else {
|
|
|
|
displayMessage(act);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public class AfterDeleteGroup : OnFinish {
|
|
|
|
GroupBaseActivity act;
|
|
|
|
|
|
|
|
public AfterDeleteGroup(Handler handler, GroupBaseActivity act):base(handler) {
|
|
|
|
this.act = act;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void run() {
|
|
|
|
if ( mSuccess) {
|
|
|
|
act.refreshIfDirty();
|
|
|
|
} else {
|
|
|
|
mHandler.Post( () => {
|
|
|
|
Toast.MakeText(act, "Unrecoverable error: " + mMessage, ToastLength.Long).Show();
|
|
|
|
});
|
|
|
|
|
|
|
|
App.setShutdown();
|
|
|
|
act.Finish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|