display reason why file is read only when first opening a database

This commit is contained in:
Philipp Crocoll 2016-01-13 05:13:02 +01:00
parent cd5fc13939
commit 64e265b2be
10 changed files with 86 additions and 13 deletions

View File

@ -206,12 +206,15 @@ namespace keepass2android.Io
return _ctx.ContentResolver.PersistedUriPermissions.Any(p => p.Uri.ToString().Equals(ioc.Path)); return _ctx.ContentResolver.PersistedUriPermissions.Any(p => p.Uri.ToString().Equals(ioc.Path));
} }
public bool IsReadOnly(IOConnectionInfo ioc)
public bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null)
{ {
//on pre-Kitkat devices, we can't write content:// files //on pre-Kitkat devices, we can't write content:// files
if (!IsKitKatOrLater) if (!IsKitKatOrLater)
{ {
Kp2aLog.Log("File is read-only because we're not on KitKat or later."); Kp2aLog.Log("File is read-only because we're not on KitKat or later.");
if (reason != null)
reason.Result = UiStringKey.ReadOnlyReason_PreKitKat;
return true; return true;
} }
@ -226,7 +229,12 @@ namespace keepass2android.Io
{ {
int flags = cursor.GetInt(cursor.GetColumnIndex(DocumentsContract.Document.ColumnFlags)); int flags = cursor.GetInt(cursor.GetColumnIndex(DocumentsContract.Document.ColumnFlags));
Kp2aLog.Log("File flags: " + flags); Kp2aLog.Log("File flags: " + flags);
return (flags & (long) DocumentContractFlags.SupportsWrite) == 0; if ((flags & (long) DocumentContractFlags.SupportsWrite) == 0)
{
if (reason != null)
reason.Result = UiStringKey.ReadOnlyReason_ReadOnlyFlag;
return true;
}
} }
} }
finally finally

View File

@ -370,14 +370,24 @@ namespace keepass2android.Io
} }
public bool IsReadOnly(IOConnectionInfo ioc) public bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null)
{ {
if (ioc.IsLocalFile()) if (ioc.IsLocalFile())
{ {
if (IsLocalFileFlaggedReadOnly(ioc)) if (IsLocalFileFlaggedReadOnly(ioc))
{
if (reason != null)
reason.Result = UiStringKey.ReadOnlyReason_ReadOnlyFlag;
return true; return true;
}
if (IsReadOnlyBecauseKitkatRestrictions(ioc)) if (IsReadOnlyBecauseKitkatRestrictions(ioc))
{
if (reason != null)
reason.Result = UiStringKey.ReadOnlyReason_ReadOnlyKitKat;
return true; return true;
}
return false; return false;
} }

View File

@ -556,11 +556,11 @@ namespace keepass2android.Io
return _cachedStorage.IsPermanentLocation(ioc); return _cachedStorage.IsPermanentLocation(ioc);
} }
public bool IsReadOnly(IOConnectionInfo ioc) public bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null)
{ {
//even though the cache can always be written, the changes made in the cache could not be transferred to the cached file //even though the cache can always be written, the changes made in the cache could not be transferred to the cached file
//so we better treat the cache as read-only as well. //so we better treat the cache as read-only as well.
return _cachedStorage.IsReadOnly(ioc); return _cachedStorage.IsReadOnly(ioc, reason);
} }
private void StoreFilePath(IOConnectionInfo folderPath, string filename, IOConnectionInfo res) private void StoreFilePath(IOConnectionInfo folderPath, string filename, IOConnectionInfo res)

View File

@ -15,6 +15,11 @@ namespace keepass2android.Io
FileChooserPrepared = FullFilenameSelected + 1, FileChooserPrepared = FullFilenameSelected + 1,
FileUsagePrepared = FileChooserPrepared + 1 FileUsagePrepared = FileChooserPrepared + 1
} }
public class OptionalOut<T>
{
public T Result { get; set; }
}
public static class FileStorageSetupDefs public static class FileStorageSetupDefs
{ {
@ -165,10 +170,14 @@ namespace keepass2android.Io
/// Does not require to exist forever! /// Does not require to exist forever!
bool IsPermanentLocation(IOConnectionInfo ioc); bool IsPermanentLocation(IOConnectionInfo ioc);
/// <summary> /// <summary>
/// Should return true if the file cannot be written. /// Should return true if the file cannot be written.
/// </summary> /// </summary>
bool IsReadOnly(IOConnectionInfo ioc); bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null );
} }
public interface IPermissionRequestingFileStorage public interface IPermissionRequestingFileStorage

View File

@ -289,7 +289,7 @@ namespace keepass2android.Io
return true; return true;
} }
public bool IsReadOnly(IOConnectionInfo ioc) public bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null)
{ {
return false; //TODO implement. note, however, that we MAY return false even if it's read-only return false; //TODO implement. note, however, that we MAY return false even if it's read-only
} }

View File

@ -176,9 +176,9 @@ namespace keepass2android.Io
return _baseStorage.IsPermanentLocation(ioc); return _baseStorage.IsPermanentLocation(ioc);
} }
public bool IsReadOnly(IOConnectionInfo ioc) public bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null)
{ {
return _baseStorage.IsReadOnly(ioc); return _baseStorage.IsReadOnly(ioc, reason);
} }
public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode, public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode,

View File

@ -81,6 +81,9 @@ namespace keepass2android
TemplateTitle_Membership, TemplateTitle_Membership,
TemplateGroupName, TemplateGroupName,
AskAddTemplatesTitle, AskAddTemplatesTitle,
AskAddTemplatesMessage AskAddTemplatesMessage,
ReadOnlyReason_PreKitKat,
ReadOnlyReason_ReadOnlyFlag,
ReadOnlyReason_ReadOnlyKitKat
} }
} }

View File

@ -153,12 +153,24 @@ namespace keepass2android
public static string GetFingerprintPrefKey(IOConnectionInfo ioc) public static string GetFingerprintPrefKey(IOConnectionInfo ioc)
{ {
SHA256Managed sha256 = new SHA256Managed(); var iocAsHexString = IocAsHexString(ioc);
string iocAsHexString = MemUtil.ByteArrayToHexString(sha256.ComputeHash(Encoding.Unicode.GetBytes(ioc.Path.ToCharArray())));
return "kp2a_ioc_" + iocAsHexString; return "kp2a_ioc_" + iocAsHexString;
} }
public string IocAsHexString()
{
return IocAsHexString(Ioc);
}
private static string IocAsHexString(IOConnectionInfo ioc)
{
SHA256Managed sha256 = new SHA256Managed();
string iocAsHexString =
MemUtil.ByteArrayToHexString(sha256.ComputeHash(Encoding.Unicode.GetBytes(ioc.Path.ToCharArray())));
return iocAsHexString;
}
public static string GetFingerprintModePrefKey(IOConnectionInfo ioc) public static string GetFingerprintModePrefKey(IOConnectionInfo ioc)
{ {
return GetFingerprintPrefKey(ioc) + "_mode"; return GetFingerprintPrefKey(ioc) + "_mode";

View File

@ -35,6 +35,7 @@ using Android.Preferences;
using Android.Runtime; using Android.Runtime;
using Android.Support.V4.View; using Android.Support.V4.View;
using Android.Support.V7.App; using Android.Support.V7.App;
using keepass2android.Io;
using KeePassLib.Security; using KeePassLib.Security;
using AlertDialog = Android.App.AlertDialog; using AlertDialog = Android.App.AlertDialog;
using Object = Java.Lang.Object; using Object = Java.Lang.Object;
@ -255,9 +256,32 @@ namespace keepass2android
SetGroupTitle(); SetGroupTitle();
SetGroupIcon(); SetGroupIcon();
FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group); FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group);
Log.Warn(Tag, "Finished creating group"); Log.Warn(Tag, "Finished creating group");
var ioc = App.Kp2a.GetDb().Ioc;
OptionalOut<UiStringKey> reason = new OptionalOut<UiStringKey>();
if (App.Kp2a.GetFileStorage(ioc).IsReadOnly(ioc, reason))
{
bool hasShownReadOnlyReason =
PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(App.Kp2a.GetDb().IocAsHexString() + "_readonlyreason", false);
if (!hasShownReadOnlyReason)
{
var b = new AlertDialog.Builder(this);
b.SetTitle(Resource.String.FileReadOnlyTitle);
b.SetMessage(GetString(Resource.String.FileReadOnlyMessagePre) + " " + App.Kp2a.GetResourceString(reason.Result));
b.SetPositiveButton(Android.Resource.String.Ok,
(sender, args) =>
{
PreferenceManager.GetDefaultSharedPreferences(this).
Edit().PutBoolean(App.Kp2a.GetDb().IocAsHexString() + "_readonlyreason", true).Commit();
});
b.Show();
}
}
} }

View File

@ -573,6 +573,13 @@
<string name="ClickOkToSelectLocation">Click OK to select a location where the file should be copied.</string> <string name="ClickOkToSelectLocation">Click OK to select a location where the file should be copied.</string>
<string name="CancelReadOnly">Cancel, open read-only.</string> <string name="CancelReadOnly">Cancel, open read-only.</string>
<string name="FileReadOnlyTitle">Database is read-only</string>
<string name="FileReadOnlyMessagePre">Keepass2Android has opened the current database in read-only mode.</string>
<string name="ReadOnlyReason_PreKitKat">It seems like you opened the file from an external app. This way does not support writing. If you want to make changes to the database, please close the database and select Change database. Then open the file from one of the available options if possible.</string>
<string name="ReadOnlyReason_ReadOnlyFlag">The read-only flag is set. Remove this flag if you want to make changes to the database.</string>
<string name="ReadOnlyReason_ReadOnlyKitKat">Writing is not possible because of restrictions introduced in Android KitKat. If you want to make changes to the database, close the database and select Change database. Then open the file using System file picker.</string>
<string name="AddCustomIcon">Add icon from file...</string> <string name="AddCustomIcon">Add icon from file...</string>
<string name="CopyingFile">Copying file...</string> <string name="CopyingFile">Copying file...</string>