Merge branch 'master' of https://git01.codeplex.com/keepass2android into HEAD
Conflicts: src/java/JavaFileStorage/bin/javafilestorage.jar
This commit is contained in:
200
src/Kp2aBusinessLogic/Io/AndroidContentStorage.cs
Normal file
200
src/Kp2aBusinessLogic/Io/AndroidContentStorage.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
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.Serialization;
|
||||
|
||||
namespace keepass2android.Io
|
||||
{
|
||||
//TODOC,TOTEST, TODO: unimplemented methods?
|
||||
public class AndroidContentStorage: IFileStorage
|
||||
{
|
||||
private readonly Context _ctx;
|
||||
|
||||
public AndroidContentStorage(Context ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
public IEnumerable<string> SupportedProtocols
|
||||
{
|
||||
get { yield return "content"; }
|
||||
}
|
||||
|
||||
public void Delete(IOConnectionInfo ioc)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Stream OpenFileForRead(IOConnectionInfo ioc)
|
||||
{
|
||||
return _ctx.ContentResolver.OpenInputStream(Android.Net.Uri.Parse(ioc.Path));
|
||||
}
|
||||
|
||||
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||
{
|
||||
return new AndroidContentWriteTransaction(ioc.Path, _ctx);
|
||||
}
|
||||
|
||||
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public bool RequiresCredentials(IOConnectionInfo ioc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FileDescription GetFileDescription(IOConnectionInfo ioc)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool RequiresSetup(IOConnectionInfo ioConnection)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public string IocToPath(IOConnectionInfo ioc)
|
||||
{
|
||||
return ioc.Path;
|
||||
}
|
||||
|
||||
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
activity.IocToIntent(intent, new IOConnectionInfo() { Path = protocolId + "://" });
|
||||
activity.OnImmediateResult(requestCode, (int)FileStorageResults.FileChooserPrepared, intent);
|
||||
}
|
||||
|
||||
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
|
||||
bool alwaysReturnSuccess)
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
activity.IocToIntent(intent, ioc);
|
||||
activity.OnImmediateResult(requestCode, (int)FileStorageResults.FileUsagePrepared, intent);
|
||||
}
|
||||
|
||||
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnResume(IFileStorageSetupActivity activity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnStart(IFileStorageSetupActivity activity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string GetDisplayName(IOConnectionInfo ioc)
|
||||
{
|
||||
return ioc.Path;
|
||||
}
|
||||
|
||||
public string CreateFilePath(string parent, string newFilename)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||
{
|
||||
//on pre-Kitkat devices, content:// is always temporary:
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsReadOnly(IOConnectionInfo ioc)
|
||||
{
|
||||
//on pre-Kitkat devices, we can't write content:// files
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidContentWriteTransaction : IWriteTransaction
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly Context _ctx;
|
||||
private MemoryStream _memoryStream;
|
||||
|
||||
public AndroidContentWriteTransaction(string path, Context ctx)
|
||||
{
|
||||
_path = path;
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_memoryStream.Dispose();
|
||||
}
|
||||
|
||||
public Stream OpenFile()
|
||||
{
|
||||
_memoryStream = new MemoryStream();
|
||||
return _memoryStream;
|
||||
}
|
||||
|
||||
public void CommitWrite()
|
||||
{
|
||||
using (Stream outputStream = _ctx.ContentResolver.OpenOutputStream(Android.Net.Uri.Parse(_path)))
|
||||
{
|
||||
outputStream.Write(_memoryStream.ToArray(), 0, (int)_memoryStream.Length);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Security;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Java.Security.Cert;
|
||||
@@ -295,5 +296,67 @@ namespace keepass2android.Io
|
||||
res.Path += filename;
|
||||
return res;
|
||||
}
|
||||
|
||||
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsReadOnlyBecauseKitkatRestrictions(IOConnectionInfo ioc)
|
||||
{
|
||||
if (IsLocalFileFlaggedReadOnly(ioc))
|
||||
return false; //it's not read-only because of the restrictions introduced in kitkat
|
||||
try
|
||||
{
|
||||
//test if we can open
|
||||
//http://www.doubleencore.com/2014/03/android-external-storage/#comment-1294469517
|
||||
using (var writer = new Java.IO.FileOutputStream(ioc.Path, true))
|
||||
{
|
||||
writer.Close();
|
||||
return false; //we can write
|
||||
}
|
||||
}
|
||||
catch (Java.IO.IOException)
|
||||
{
|
||||
//seems like we can't write to that location even though it's not read-only
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public bool IsReadOnly(IOConnectionInfo ioc)
|
||||
{
|
||||
if (ioc.IsLocalFile())
|
||||
{
|
||||
if (IsLocalFileFlaggedReadOnly(ioc))
|
||||
return true;
|
||||
if (IsReadOnlyBecauseKitkatRestrictions(ioc))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
//for remote files assume they can be written: (think positive! :-) )
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsLocalFileFlaggedReadOnly(IOConnectionInfo ioc)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new FileInfo(ioc.Path).IsReadOnly;
|
||||
}
|
||||
catch (SecurityException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -488,6 +488,11 @@ namespace keepass2android.Io
|
||||
_cachedStorage.PrepareFileUsage(activity, ioc, requestCode, alwaysReturnSuccess || IsCached(ioc));
|
||||
}
|
||||
|
||||
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
|
||||
{
|
||||
_cachedStorage.PrepareFileUsage(ctx, ioc);
|
||||
}
|
||||
|
||||
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||
{
|
||||
_cachedStorage.OnCreate(activity, savedInstanceState);
|
||||
@@ -542,6 +547,19 @@ namespace keepass2android.Io
|
||||
|
||||
}
|
||||
|
||||
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||
{
|
||||
//even though the cache would be permanent, it's not a good idea to cache a temporary file, so return false in that case:
|
||||
return _cachedStorage.IsPermanentLocation(ioc);
|
||||
}
|
||||
|
||||
public bool IsReadOnly(IOConnectionInfo ioc)
|
||||
{
|
||||
//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.
|
||||
return _cachedStorage.IsReadOnly(ioc);
|
||||
}
|
||||
|
||||
private void StoreFilePath(IOConnectionInfo folderPath, string filename, IOConnectionInfo res)
|
||||
{
|
||||
File.WriteAllText(CachedFilePath(GetPseudoIoc(folderPath, filename)) + ".filepath", res.Path);
|
||||
|
||||
@@ -157,6 +157,17 @@ namespace keepass2android.Io
|
||||
/// </summary>
|
||||
/// The method may throw FileNotFoundException or not in case the file doesn't exist.
|
||||
IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename);
|
||||
|
||||
/// <summary>
|
||||
/// returns true if it can be expected that this location will be available permanently (in contrast to a cache copy or temporary URI permissions in Android)
|
||||
/// </summary>
|
||||
/// Does not require to exist forever!
|
||||
bool IsPermanentLocation(IOConnectionInfo ioc);
|
||||
|
||||
/// <summary>
|
||||
/// Should return true if the file cannot be written.
|
||||
/// </summary>
|
||||
bool IsReadOnly(IOConnectionInfo ioc);
|
||||
}
|
||||
|
||||
public interface IWriteTransaction: IDisposable
|
||||
|
||||
@@ -283,6 +283,16 @@ namespace keepass2android.Io
|
||||
|
||||
}
|
||||
|
||||
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsReadOnly(IOConnectionInfo ioc)
|
||||
{
|
||||
return false; //TODO implement. note, however, that we MAY return false even if it's read-only
|
||||
}
|
||||
|
||||
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||
{
|
||||
_jfs.OnCreate(((IJavaFileStorageFileStorageSetupActivity)activity), savedInstanceState);
|
||||
|
||||
Reference in New Issue
Block a user