added offline mode

This commit is contained in:
Philipp Crocoll 2015-12-27 08:50:45 +01:00
parent d2850f5ea6
commit ec4fe32b29
14 changed files with 776 additions and 346 deletions

View File

@ -54,19 +54,21 @@ namespace keepass2android.Io
void LoadedFromRemoteInSync(IOConnectionInfo ioc);
}
/// <summary>
/// Implements the IFileStorage interface as a proxy: A base storage is used as a remote storage. Local files are used to cache the
/// files on remote.
/// </summary>
public class CachingFileStorage: IFileStorage
public class CachingFileStorage : IFileStorage, IOfflineSwitchable
{
protected readonly IFileStorage _cachedStorage;
protected readonly OfflineSwitchableFileStorage _cachedStorage;
private readonly ICacheSupervisor _cacheSupervisor;
private readonly string _streamCacheDir;
public CachingFileStorage(IFileStorage cachedStorage, string cacheDir, ICacheSupervisor cacheSupervisor)
{
_cachedStorage = cachedStorage;
_cachedStorage = new OfflineSwitchableFileStorage(cachedStorage);
_cacheSupervisor = cacheSupervisor;
_streamCacheDir = cacheDir + Java.IO.File.Separator + "OfflineCache" + Java.IO.File.Separator;
if (!Directory.Exists(_streamCacheDir))
@ -610,5 +612,11 @@ namespace keepass2android.Io
return File.OpenRead(CachedFilePath(ioc));
}
}
public bool IsOffline
{
get { return _cachedStorage.IsOffline; }
set { _cachedStorage.IsOffline = value; }
}
}
}

View File

@ -0,0 +1,191 @@
using System;
using System.Collections.Generic;
using System.IO;
using Android.Content;
using Android.OS;
using KeePassLib.Serialization;
namespace keepass2android.Io
{
public interface IOfflineSwitchable
{
bool IsOffline { get; set; }
}
/// <summary>
/// Encapsulates another IFileStorage. Allows to switch to offline mode by throwing
/// an exception when trying to read or write a file.
/// </summary>
public class OfflineSwitchableFileStorage : IFileStorage, IOfflineSwitchable
{
private readonly IFileStorage _baseStorage;
public bool IsOffline { get; set; }
public OfflineSwitchableFileStorage(IFileStorage baseStorage)
{
_baseStorage = baseStorage;
}
public IEnumerable<string> SupportedProtocols
{
get { return _baseStorage.SupportedProtocols; }
}
public void Delete(IOConnectionInfo ioc)
{
_baseStorage.Delete(ioc);
}
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
{
return _baseStorage.CheckForFileChangeFast(ioc, previousFileVersion);
}
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
{
return _baseStorage.GetCurrentFileVersionFast(ioc);
}
public Stream OpenFileForRead(IOConnectionInfo ioc)
{
AssertOnline();
return _baseStorage.OpenFileForRead(ioc);
}
private void AssertOnline()
{
if (IsOffline)
{
//throw new Exception(_app.GetResourceString(UiStringKey.InOfflineMode));
throw new OfflineModeException();
}
}
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
{
AssertOnline();
return _baseStorage.OpenWriteTransaction(ioc, useFileTransaction);
}
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
{
return _baseStorage.GetFilenameWithoutPathAndExt(ioc);
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
return _baseStorage.RequiresCredentials(ioc);
}
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
{
_baseStorage.CreateDirectory(ioc, newDirName);
}
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
{
return _baseStorage.ListContents(ioc);
}
public FileDescription GetFileDescription(IOConnectionInfo ioc)
{
return _baseStorage.GetFileDescription(ioc);
}
public bool RequiresSetup(IOConnectionInfo ioConnection)
{
if (IsOffline)
return false;
return _baseStorage.RequiresSetup(ioConnection);
}
public string IocToPath(IOConnectionInfo ioc)
{
return _baseStorage.IocToPath(ioc);
}
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
{
_baseStorage.StartSelectFile(activity, isForSave, requestCode, protocolId);
}
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
bool alwaysReturnSuccess)
{
if (IsOffline)
{
Intent intent = new Intent();
activity.IocToIntent(intent, ioc);
activity.OnImmediateResult(requestCode, (int)FileStorageResults.FileUsagePrepared, intent);
return;
}
_baseStorage.PrepareFileUsage(activity, ioc, requestCode, alwaysReturnSuccess);
}
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
{
if (IsOffline)
return;
_baseStorage.PrepareFileUsage(ctx, ioc);
}
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
{
_baseStorage.OnCreate(activity, savedInstanceState);
}
public void OnResume(IFileStorageSetupActivity activity)
{
_baseStorage.OnResume(activity);
}
public void OnStart(IFileStorageSetupActivity activity)
{
_baseStorage.OnStart(activity);
}
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
{
_baseStorage.OnActivityResult(activity, requestCode, resultCode, data);
}
public string GetDisplayName(IOConnectionInfo ioc)
{
return _baseStorage.GetDisplayName(ioc);
}
public string CreateFilePath(string parent, string newFilename)
{
return _baseStorage.CreateFilePath(parent, newFilename);
}
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
{
return _baseStorage.GetParentPath(ioc);
}
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
{
return _baseStorage.GetFilePath(folderPath, filename);
}
public bool IsPermanentLocation(IOConnectionInfo ioc)
{
return _baseStorage.IsPermanentLocation(ioc);
}
public bool IsReadOnly(IOConnectionInfo ioc)
{
return _baseStorage.IsReadOnly(ioc);
}
}
public class OfflineModeException : Exception
{
public override string Message
{
get { return "Working offline."; }
}
}
}

View File

@ -79,6 +79,7 @@
<Compile Include="Io\IFileStorage.cs" />
<Compile Include="Io\IoUtil.cs" />
<Compile Include="Io\JavaFileStorage.cs" />
<Compile Include="Io\OfflineSwitchableFileStorage.cs" />
<Compile Include="Io\SftpFileStorage.cs" />
<Compile Include="Io\SkyDriveFileStorage.cs" />
<Compile Include="IProgressDialog.cs" />

View File

@ -61,6 +61,7 @@ namespace keepass2android
DuplicateUuidsErrorAdditional,
DeletingItems,
AskDeletePermanentlyItems,
AskDeletePermanentlyItemsNoRecycle
AskDeletePermanentlyItemsNoRecycle,
InOfflineMode
}
}

View File

@ -41,7 +41,8 @@ using Object = Java.Lang.Object;
namespace keepass2android
{
public abstract class GroupBaseActivity : LockCloseActivity {
public abstract class GroupBaseActivity : LockCloseActivity
{
public const String KeyEntry = "entry";
public const String KeyMode = "mode";
@ -168,12 +169,17 @@ namespace keepass2android
internal AppTask AppTask;
private String strCachedGroupUuid = null;
private IMenuItem _offlineItem;
private IMenuItem _onlineItem;
private IMenuItem _syncItem;
public String UuidGroup {
get {
if (strCachedGroupUuid == null) {
public String UuidGroup
{
get
{
if (strCachedGroupUuid == null)
{
strCachedGroupUuid = MemUtil.ByteArrayToHexString(Group.Uuid.UuidBytes);
}
return strCachedGroupUuid;
@ -181,7 +187,8 @@ namespace keepass2android
}
protected override void OnResume() {
protected override void OnResume()
{
base.OnResume();
_design.ReapplyTheme();
AppTask.StartInGroupActivity(this);
@ -198,9 +205,11 @@ namespace keepass2android
return true;
}
public void RefreshIfDirty() {
public void RefreshIfDirty()
{
Database db = App.Kp2a.GetDb();
if ( db.Dirty.Contains(Group) ) {
if (db.Dirty.Contains(Group))
{
db.Dirty.Remove(Group);
ListAdapter.NotifyDataSetChanged();
@ -217,7 +226,8 @@ namespace keepass2android
get { return false; }
}
protected override void OnCreate(Bundle savedInstanceState) {
protected override void OnCreate(Bundle savedInstanceState)
{
_design.ApplyTheme();
base.OnCreate(savedInstanceState);
@ -226,7 +236,8 @@ namespace keepass2android
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
// Likely the app has been killed exit the activity
if ( ! App.Kp2a.GetDb().Loaded ) {
if (!App.Kp2a.GetDb().Loaded)
{
Finish();
return;
}
@ -300,7 +311,8 @@ namespace keepass2android
if (!String.IsNullOrEmpty(name))
{
titleText = name;
} else
}
else
{
titleText = GetText(Resource.String.root);
}
@ -316,8 +328,10 @@ namespace keepass2android
}
protected void SetGroupIcon() {
if (Group != null) {
protected void SetGroupIcon()
{
if (Group != null)
{
Drawable drawable = App.Kp2a.GetDb().DrawableFactory.GetIconDrawable(this, App.Kp2a.GetDb().KpDatabase, Group.IconId, Group.CustomIconUuid, true);
SupportActionBar.SetDisplayShowHomeEnabled(true);
//SupportActionBar.SetIcon(drawable);
@ -384,7 +398,8 @@ namespace keepass2android
}
}
public override bool OnCreateOptionsMenu(IMenu menu) {
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater inflater = MenuInflater;
inflater.Inflate(Resource.Menu.group, menu);
@ -397,28 +412,57 @@ namespace keepass2android
searchView.SetOnSuggestionListener(new SuggestionListener(searchView.SuggestionsAdapter, this, searchItem));
searchView.SetOnQueryTextListener(new OnQueryTextListener(this));
ActionBar.LayoutParams lparams = new ActionBar.LayoutParams(ActionBar.LayoutParams.MatchParent, ActionBar.LayoutParams.MatchParent);
ActionBar.LayoutParams lparams = new ActionBar.LayoutParams(ActionBar.LayoutParams.MatchParent,
ActionBar.LayoutParams.MatchParent);
searchView.LayoutParameters = lparams;
var item = menu.FindItem(Resource.Id.menu_sync);
if (item != null)
{
if (App.Kp2a.GetDb().Ioc.IsLocalFile())
item.SetVisible(false);
else
item.SetVisible(true);
}
_syncItem = menu.FindItem(Resource.Id.menu_sync);
_offlineItem = menu.FindItem(Resource.Id.menu_work_offline);
_onlineItem = menu.FindItem(Resource.Id.menu_work_online);
UpdateOfflineModeMenu();
return base.OnCreateOptionsMenu(menu);
}
private void UpdateOfflineModeMenu()
{
if (_syncItem != null)
{
if (App.Kp2a.GetDb().Ioc.IsLocalFile())
_syncItem.SetVisible(false);
else
_syncItem.SetVisible(!App.Kp2a.OfflineMode);
}
if (App.Kp2a.GetFileStorage(App.Kp2a.GetDb().Ioc) is IOfflineSwitchable)
{
if (_offlineItem != null)
_offlineItem.SetVisible(App.Kp2a.OfflineMode == false);
if (_onlineItem != null)
_onlineItem.SetVisible(App.Kp2a.OfflineMode);
}
else
{
if (_offlineItem != null)
_offlineItem.SetVisible(false);
if (_onlineItem != null)
_onlineItem.SetVisible(false);
public override bool OnPrepareOptionsMenu(IMenu menu) {
if ( ! base.OnPrepareOptionsMenu(menu) ) {
}
}
public override bool OnPrepareOptionsMenu(IMenu menu)
{
if (!base.OnPrepareOptionsMenu(menu))
{
return false;
}
@ -428,8 +472,10 @@ namespace keepass2android
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item) {
switch ( item.ItemId ) {
public override bool OnOptionsItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
case Resource.Id.menu_donate:
return Util.GotoDonateUrl(this);
case Resource.Id.menu_lock:
@ -449,6 +495,18 @@ namespace keepass2android
Synchronize();
return true;
case Resource.Id.menu_work_offline:
App.Kp2a.OfflineMode = App.Kp2a.OfflineModePreference = true;
UpdateOfflineModeMenu();
return true;
case Resource.Id.menu_work_online:
App.Kp2a.OfflineMode = App.Kp2a.OfflineModePreference = false;
UpdateOfflineModeMenu();
Synchronize();
return true;
case Resource.Id.menu_sort:
ChangeSort();
return true;
@ -470,7 +528,8 @@ namespace keepass2android
{
private readonly IOConnectionInfo _ioc;
public SyncOtpAuxFile(IOConnectionInfo ioc) : base(null)
public SyncOtpAuxFile(IOConnectionInfo ioc)
: base(null)
{
_ioc = ioc;
}
@ -582,33 +641,48 @@ namespace keepass2android
}
public class RefreshTask : OnFinish {
public class RefreshTask : OnFinish
{
readonly GroupBaseActivity _act;
public RefreshTask(Handler handler, GroupBaseActivity act):base(handler) {
public RefreshTask(Handler handler, GroupBaseActivity act)
: base(handler)
{
_act = act;
}
public override void Run() {
if ( Success) {
public override void Run()
{
if (Success)
{
_act.RefreshIfDirty();
} else {
}
else
{
DisplayMessage(_act);
}
}
}
public class AfterDeleteGroup : OnFinish {
public class AfterDeleteGroup : OnFinish
{
readonly GroupBaseActivity _act;
public AfterDeleteGroup(Handler handler, GroupBaseActivity act):base(handler) {
public AfterDeleteGroup(Handler handler, GroupBaseActivity act)
: base(handler)
{
_act = act;
}
public override void Run() {
if ( Success) {
public override void Run()
{
if (Success)
{
_act.RefreshIfDirty();
} else {
Handler.Post( () => {
}
else
{
Handler.Post(() =>
{
Toast.MakeText(_act, "Unrecoverable error: " + Message, ToastLength.Long).Show();
});

View File

@ -102,6 +102,8 @@ namespace keepass2android
private Task<MemoryStream> _loadDbTask;
private bool _loadDbTaskOffline; //indicate if preloading was started with offline mode
private IOConnectionInfo _ioConnection;
private String _keyFileOrProvider;
bool _showPassword;
@ -688,6 +690,7 @@ namespace keepass2android
private MeasuringRelativeLayout.MeasureArgs _measureArgs;
private ActivityDesign _activityDesign;
internal class MyActionBarDrawerToggle : ActionBarDrawerToggle
{
PasswordActivity owner;
@ -862,7 +865,7 @@ namespace keepass2android
InitializeTogglePasswordButton();
InitializeKeyfileBrowseButton();
InitializeQuickUnlockCheckbox();
InitializeOptionCheckboxes();
RestoreState(savedInstanceState);
@ -1270,6 +1273,17 @@ namespace keepass2android
CheckBox cbQuickUnlock = (CheckBox) FindViewById(Resource.Id.enable_quickunlock);
App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked);
if (App.Kp2a.OfflineMode != _loadDbTaskOffline)
{
//keep the loading result if we loaded in online-mode (now offline) and the task is completed
if (!App.Kp2a.OfflineMode || !_loadDbTask.IsCompleted)
{
//discard the pre-loading task
_loadDbTask = null;
}
}
//avoid password being visible while loading:
_showPassword = false;
MakePasswordMaskedOrVisible();
@ -1577,6 +1591,20 @@ namespace keepass2android
base.OnResume();
_activityDesign.ReapplyTheme();
CheckBox cbOfflineMode = (CheckBox)FindViewById(Resource.Id.work_offline);
App.Kp2a.OfflineMode = cbOfflineMode.Checked = App.Kp2a.OfflineModePreference; //this won't overwrite new user settings because every change is directly saved in settings
LinearLayout offlineModeContainer = FindViewById<LinearLayout>(Resource.Id.work_offline_container);
if (App.Kp2a.GetFileStorage(_ioConnection) is IOfflineSwitchable)
{
offlineModeContainer.Visibility = ViewStates.Visible;
}
else
{
offlineModeContainer.Visibility = ViewStates.Gone;
App.Kp2a.OfflineMode = false;
}
EditText pwd = FindViewById<EditText>(Resource.Id.password_edit);
pwd.PostDelayed(() =>
{
@ -1650,14 +1678,22 @@ namespace keepass2android
{
// Create task to kick off file loading while the user enters the password
_loadDbTask = Task.Factory.StartNew<MemoryStream>(PreloadDbFile);
_loadDbTaskOffline = App.Kp2a.OfflineMode;
}
}
}
}
private void InitializeQuickUnlockCheckbox() {
private void InitializeOptionCheckboxes() {
CheckBox cbQuickUnlock = (CheckBox)FindViewById(Resource.Id.enable_quickunlock);
cbQuickUnlock.Checked = _prefs.GetBoolean(GetString(Resource.String.QuickUnlockDefaultEnabled_key), true);
CheckBox cbOfflineMode = (CheckBox)FindViewById(Resource.Id.work_offline);
cbOfflineMode.CheckedChange += (sender, args) =>
{
App.Kp2a.OfflineModePreference = App.Kp2a.OfflineMode = args.IsChecked;
};
}
private String GetKeyFile(String filename) {

View File

@ -308,12 +308,51 @@
android:layout_width="fill_parent"
android:layout_marginTop="16dp"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/enable_quickunlock_container"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/enable_quickunlock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/enable_quickunlock" />
<keepass2android.views.Kp2aShortHelpView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance_Help_Dense"
android:textColor="@color/light_gray"
app:help_text="@string/help_quickunlock"
app:title_text="@string/enable_quickunlock"
android:text="@string/help_quickunlock"
android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
<LinearLayout
android:id="@+id/work_offline_container"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/work_offline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/UseOfflineMode" />
<keepass2android.views.Kp2aShortHelpView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance_Help_Dense"
android:textColor="@color/light_gray"
app:help_text="@string/UseOfflineMode_Info"
app:title_text="@string/UseOfflineMode"
android:text="@string/UseOfflineMode_Info"
android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
<View
android:id="@+id/spacing"
android:layout_width="fill_parent"

View File

@ -47,6 +47,14 @@
android:icon="@drawable/ic_popup_sync"
android:title="@string/synchronize_database_menu"
app:showAsAction="never"
/>
<item android:id="@+id/menu_work_offline"
android:title="@string/UseOfflineMode"
app:showAsAction="never"
/>
<item android:id="@+id/menu_work_online"
android:title="@string/UseOnlineMode"
app:showAsAction="never"
/>
<item android:id="@+id/menu_sort"

View File

@ -20,9 +20,11 @@
<resources>
<attr name="help_text" format="string" />
<attr name="title_text" format="string" />
<declare-styleable name="Kp2aShortHelpView">
<attr name="android:text"/>
<attr name="help_text" />
<attr name="title_text" />
</declare-styleable>
<declare-styleable name="TextWithHelp">
<attr name="android:text"/>
@ -88,6 +90,8 @@
<string name="FileHandling_prefs_key">FileHandling_prefs_key</string>
<string name="keyboardswitch_prefs_key">keyboardswitch_prefs_key</string>
<string name="OfflineMode_key">OfflineMode_key</string>
<string name="QuickUnlockDefaultEnabled_key">Enable_QuickUnlock_by_default</string>
<string name="QuickUnlockLength_key">QuickUnlockLength</string>
<string name="QuickUnlockLength_default">3</string>

View File

@ -387,6 +387,11 @@
<string name="YesSynchronize">Yes, merge</string>
<string name="NoOverwrite">No, overwrite</string>
<string name="UseOfflineMode">Work offline</string>
<string name="UseOnlineMode">Work online</string>
<string name="UseOfflineMode_Info">Avoid any network traffic by using the local cache copy of the file. Changes are stored in the local cache only and will only be uploaded when switching back to online mode.</string>
<string name="InOfflineMode">Working offline.</string>
<string name="SynchronizingCachedDatabase">Synchronizing cached database…</string>
<string name="DownloadingRemoteFile">Downloading remote file…</string>
<string name="UploadingFile">Uploading file…</string>
@ -454,6 +459,8 @@
<string name="hint_database_location">Select where you want to store the database:</string>
<string name="button_change_location">Change location</string>
<string name="help_quickunlock">If enabled, Keepass2Android stays running in the background even when the database is locked. This allows to unlock the database later with only a short part of the master password.</string>
<string name="master_password">Master password</string>
<string name="help_master_password">Your database is encrypted with the password you enter here. Choose a strong password in order to keep the database safe! Tip: Make up a sentence or two and use the first letters of the words as password. Include punctuation marks.</string>
<string name="hint_master_password">Select a master password to protect your database:</string>

View File

@ -116,6 +116,19 @@
</style>
<style name="TextAppearance_Help_Dense">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
<item name="android:textSize">18sp</item>
<item name="android:paddingLeft">6sp</item>
<item name="android:paddingRight">6sp</item>
<item name="android:paddingBottom">0sp</item>
<item name="android:gravity">center_vertical</item>
<item name="android:layout_gravity">center_vertical</item>
<item name="android:layout_marginRight">4dip</item>
<item name="android:layout_marginLeft">4dip</item>
</style>
<style name="EditEntryButton">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
<item name="android:layout_marginTop">12dip</item>

View File

@ -410,21 +410,27 @@ namespace keepass2android
}
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache)
{
IFileStorage fileStorage;
if (iocInfo.IsLocalFile())
return new BuiltInFileStorage(this);
fileStorage = new BuiltInFileStorage(this);
else
{
IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo);
if (DatabaseCacheEnabled && allowCache)
{
return new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this);
fileStorage = new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this);
}
else
{
return innerFileStorage;
fileStorage = innerFileStorage;
}
}
if (fileStorage is IOfflineSwitchable)
{
((IOfflineSwitchable)fileStorage).IsOffline = App.Kp2a.OfflineMode;
}
return fileStorage;
}
private IFileStorage GetCloudFileStorage(IOConnectionInfo iocInfo)
@ -591,13 +597,23 @@ namespace keepass2android
public void CouldntSaveToRemote(IOConnectionInfo ioc, Exception e)
{
ShowToast(Application.Context.GetString(Resource.String.CouldNotSaveToRemote, e.Message));
var errorMessage = GetErrorMessageForFileStorageException(e);
ShowToast(Application.Context.GetString(Resource.String.CouldNotSaveToRemote, errorMessage));
}
private string GetErrorMessageForFileStorageException(Exception e)
{
string errorMessage = e.Message;
if (e is OfflineModeException)
errorMessage = GetResourceString(UiStringKey.InOfflineMode);
return errorMessage;
}
public void CouldntOpenFromRemote(IOConnectionInfo ioc, Exception ex)
{
ShowToast(Application.Context.GetString(Resource.String.CouldNotLoadFromRemote, ex.Message));
var errorMessage = GetErrorMessageForFileStorageException(ex);
ShowToast(Application.Context.GetString(Resource.String.CouldNotLoadFromRemote, errorMessage));
}
public void UpdatedCachedFileOnLoad(IOConnectionInfo ioc)
@ -667,6 +683,31 @@ namespace keepass2android
}
}
public bool OfflineModePreference
{
get
{
var prefs = PreferenceManager.GetDefaultSharedPreferences(Application.Context);
return prefs.GetBoolean(Application.Context.GetString(Resource.String.OfflineMode_key), false);
}
set
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(Application.Context);
ISharedPreferencesEditor edit = prefs.Edit();
edit.PutBoolean(Application.Context.GetString(Resource.String.OfflineMode_key), value);
edit.Commit();
}
}
/// <summary>
/// true if the app is used in offline mode
/// </summary>
public bool OfflineMode
{
get; set;
}
public void OnScreenOff()
{
if (PreferenceManager.GetDefaultSharedPreferences(Application.Context)

View File

@ -334,6 +334,7 @@ namespace keepass2android
protected override void OnResume()
{
base.OnResume();
App.Kp2a.OfflineMode = false; //no matter what the preferences are, file selection or db creation is performed offline. PasswordActivity might set this to true.
Kp2aLog.Log("FileSelect.OnResume");
_design.ReapplyTheme();

View File

@ -50,6 +50,8 @@ namespace keepass2android.views
}
}
public string TitleText { get; set; }
private void UpdateView()
{
if (!String.IsNullOrEmpty(_helpText))
@ -72,8 +74,11 @@ namespace keepass2android.views
MovementMethod = LinkMovementMethod.Instance;
Click += (sender, args) =>
{
string title = Context.GetString(AppNames.AppNameResource);
if (!string.IsNullOrEmpty(TitleText))
title = TitleText;
new AlertDialog.Builder(Context)
.SetTitle(Context.GetString(AppNames.AppNameResource))
.SetTitle(title)
.SetMessage(_helpText)
.SetPositiveButton(Android.Resource.String.Ok, (o, eventArgs) => { })
.Show();
@ -91,6 +96,7 @@ namespace keepass2android.views
TypedArray a = Context.ObtainStyledAttributes(
attrs,
Resource.Styleable.Kp2aShortHelpView);
TitleText = a.GetString(Resource.Styleable.Kp2aShortHelpView_title_text);
HelpText = a.GetString(Resource.Styleable.Kp2aShortHelpView_help_text);
}