* modified setup workflow for IFileStorage (to be compatible with Google Drive requirements)
* scheme (protocol) is always contained in path variables passed to JavaFileStorage implementors * file chooser improvements (internal browser displayed also in file chooser list e.g. when selecting an attachments, compatible with Solid Explorer content uris, removed OI stuff) * started GDrive support
This commit is contained in:
@@ -2,16 +2,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using KeePassLib.Serialization;
|
||||
using KeePassLib.Utility;
|
||||
|
||||
@@ -89,8 +82,6 @@ namespace keepass2android.Io
|
||||
return new BuiltInFileTransaction(ioc, useFileTransaction);
|
||||
}
|
||||
|
||||
public IFileStorageSetup RequiredSetup { get { return null; } }
|
||||
|
||||
public class BuiltInFileTransaction : IWriteTransaction
|
||||
{
|
||||
private readonly FileTransactionEx _transaction;
|
||||
@@ -155,5 +146,54 @@ namespace keepass2android.Io
|
||||
//TODO
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (protocolId != "file")
|
||||
activity.PerformManualFileSelect(isForSave, requestCode, protocolId);
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ using System.IO;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using KeePassLib.Cryptography;
|
||||
using KeePassLib.Serialization;
|
||||
using KeePassLib.Utility;
|
||||
@@ -19,7 +21,7 @@ namespace keepass2android.Io
|
||||
/// called when a save operation only updated the cache but not the remote file
|
||||
/// </summary>
|
||||
/// <param name="ioc">The file which we tried to write</param>
|
||||
/// <param name="e">The exception why the remote file couldn't be updated</param>
|
||||
/// <param name="ex">The exception why the remote file couldn't be updated</param>
|
||||
void CouldntSaveToRemote(IOConnectionInfo ioc, Exception ex);
|
||||
|
||||
/// <summary>
|
||||
@@ -398,8 +400,6 @@ namespace keepass2android.Io
|
||||
return new CachedWriteTransaction(ioc, useFileTransaction, this);
|
||||
}
|
||||
|
||||
public IFileStorageSetup RequiredSetup { get { return _cachedStorage.RequiredSetup; } }
|
||||
|
||||
public bool CompleteIoId()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@@ -436,6 +436,46 @@ namespace keepass2android.Io
|
||||
return _cachedStorage.GetFileDescription(ioc);
|
||||
}
|
||||
|
||||
public bool RequiresSetup(IOConnectionInfo ioConnection)
|
||||
{
|
||||
return _cachedStorage.RequiresSetup(ioConnection);
|
||||
}
|
||||
|
||||
public string IocToPath(IOConnectionInfo ioc)
|
||||
{
|
||||
return _cachedStorage.IocToPath(ioc);
|
||||
}
|
||||
|
||||
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
|
||||
{
|
||||
_cachedStorage.StartSelectFile(activity, isForSave, requestCode, protocolId);
|
||||
}
|
||||
|
||||
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode)
|
||||
{
|
||||
_cachedStorage.PrepareFileUsage(activity, ioc, requestCode);
|
||||
}
|
||||
|
||||
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||
{
|
||||
_cachedStorage.OnCreate(activity, savedInstanceState);
|
||||
}
|
||||
|
||||
public void OnResume(IFileStorageSetupActivity activity)
|
||||
{
|
||||
_cachedStorage.OnResume(activity);
|
||||
}
|
||||
|
||||
public void OnStart(IFileStorageSetupActivity activity)
|
||||
{
|
||||
_cachedStorage.OnStart(activity);
|
||||
}
|
||||
|
||||
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
_cachedStorage.OnActivityResult(activity, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
|
||||
public string GetBaseVersionHash(IOConnectionInfo ioc)
|
||||
{
|
||||
|
||||
@@ -16,17 +16,14 @@ using Keepass2android.Javafilestorage;
|
||||
|
||||
namespace keepass2android.Io
|
||||
{
|
||||
public class DropboxFileStorage: JavaFileStorage
|
||||
public partial class DropboxFileStorage: JavaFileStorage
|
||||
{
|
||||
public DropboxFileStorage(Context ctx, IKp2aApp app) :
|
||||
base(new Keepass2android.Javafilestorage.DropboxFileStorage(ctx), app)
|
||||
base(new Keepass2android.Javafilestorage.DropboxFileStorage(ctx, AppKey, AppSecret), app)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string Protocol
|
||||
{
|
||||
get { return "dropbox"; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
23
src/Kp2aBusinessLogic/Io/FileStorageSetupActivity.cs
Normal file
23
src/Kp2aBusinessLogic/Io/FileStorageSetupActivity.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
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.Serialization;
|
||||
|
||||
namespace keepass2android.Io
|
||||
{
|
||||
public interface IFileStorageSetupActivity
|
||||
{
|
||||
IOConnectionInfo Ioc { get; }
|
||||
String ProcessName { get; }
|
||||
bool IsForSave { get; }
|
||||
Bundle State { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using KeePassLib.Serialization;
|
||||
|
||||
namespace keepass2android.Io
|
||||
{
|
||||
public interface IFileStorageSetupInitiatorActivity
|
||||
{
|
||||
void StartSelectFileProcess(IOConnectionInfo ioc, bool isForSave, int requestCode);
|
||||
void StartFileUsageProcess(IOConnectionInfo ioc, int requestCode);
|
||||
void OnImmediateResult(int requestCode, int result, Intent intent);
|
||||
|
||||
Activity Activity { get; }
|
||||
|
||||
void IocToIntent(Intent intent, IOConnectionInfo ioc);
|
||||
void PerformManualFileSelect(bool isForSave, int requestCode, string protocolId);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ using KeePassLib.Serialization;
|
||||
|
||||
namespace keepass2android.Io
|
||||
{
|
||||
public class GDriveFileStorage: IFileStorage
|
||||
/*public class GDriveFileStorage: IFileStorage
|
||||
{
|
||||
public IEnumerable<string> SupportedProtocols { get { yield return "gdrive"; } }
|
||||
public void Delete(IOConnectionInfo ioc)
|
||||
@@ -42,7 +42,6 @@ namespace keepass2android.Io
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IFileStorageSetup RequiredSetup { get; private set; }
|
||||
|
||||
public bool CompleteIoId()
|
||||
{
|
||||
@@ -78,5 +77,5 @@ namespace keepass2android.Io
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -1,20 +1,33 @@
|
||||
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.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
|
||||
namespace keepass2android.Io
|
||||
{
|
||||
|
||||
public enum FileStorageResults
|
||||
{
|
||||
FullFilenameSelected = 874345 + 1,
|
||||
FileChooserPrepared = FullFilenameSelected + 1,
|
||||
FileUsagePrepared = FileChooserPrepared + 1
|
||||
}
|
||||
|
||||
public static class FileStorageSetupDefs
|
||||
{
|
||||
public static String ProcessNameSelectfile = "SELECT_FILE";
|
||||
public static String ProcessNameFileUsageSetup = "FILE_USAGE_SETUP";
|
||||
|
||||
public static String ExtraProcessName = "EXTRA_PROCESS_NAME";
|
||||
public static String ExtraPath = "PATH";
|
||||
public static String ExtraIsForSave = "IS_FOR_SAVE";
|
||||
public static String ExtraErrorMessage = "EXTRA_ERROR_MESSAGE";
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called as a callback from CheckForFileChangeAsync.
|
||||
/// </summary>
|
||||
@@ -69,14 +82,6 @@ namespace keepass2android.Io
|
||||
/// <param name="useFileTransaction">if true, force to use file system level transaction. This might be ignored if the file storage has built in transaction support</param>
|
||||
IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an instance of an implementation of IFileStorageSetup or one of the more complex interfaces.
|
||||
/// Depending on the type returned, the caller should try to follow the interface as close as possible.
|
||||
/// Returns null if the file storage is setup or doesn't require anything to work.
|
||||
/// </summary>
|
||||
/// This is due to different storage types requiring different workflows for authentication processes
|
||||
IFileStorageSetup RequiredSetup { get; }
|
||||
|
||||
/// <summary>
|
||||
/// brings up a dialog to query credentials or something like this.
|
||||
/// </summary>
|
||||
@@ -111,32 +116,36 @@ namespace keepass2android.Io
|
||||
/// returns the description of the given file
|
||||
/// </summary>
|
||||
FileDescription GetFileDescription(IOConnectionInfo ioc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base interface for required setup code
|
||||
/// </summary>
|
||||
public interface IFileStorageSetup
|
||||
{
|
||||
/// <summary>
|
||||
/// call this when the user explicitly wants to use this file storage. Might require user interaction.
|
||||
/// May throw if the setup failed permanentaly.
|
||||
/// returns true if everything is ok with connecting to the given file.
|
||||
/// Returns False if PrepareFileUsage must be called first.
|
||||
/// </summary>
|
||||
/// <returns>true if the setup was succesful immediately (without UI). Returns false if setup was not successful but no error occured or can be displayed.</returns>
|
||||
bool TrySetup(Activity activity);
|
||||
}
|
||||
bool RequiresSetup(IOConnectionInfo ioConnection);
|
||||
|
||||
/// <summary>
|
||||
/// Interface which can be used additionally for an IFileStorageSetup to indicate that setup must be completed in OnResume()
|
||||
/// </summary>
|
||||
public interface IFileStorageSetupOnResume
|
||||
{
|
||||
/// <summary>
|
||||
/// call this after TrySetup() returned false in the next OnResume()
|
||||
/// May throw if the setup failed permanentaly.
|
||||
/// converts the ioc to a path which may contain the credentials
|
||||
/// </summary>
|
||||
/// <returns>true if setup was succesful</returns>
|
||||
bool TrySetupOnResume(Activity activity);
|
||||
string IocToPath(IOConnectionInfo ioc);
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the process for choosing a file in the given file storage.
|
||||
/// The file storage should either call OnImmediateResult or StartSelectFileProcess
|
||||
/// </summary>
|
||||
void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId);
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the process for choosing a file in the given file storage.
|
||||
/// The file storage should either call OnImmediateResult or StartFileUsageProcess
|
||||
/// </summary>
|
||||
void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode);
|
||||
|
||||
//Setup methods: these are called from the setup activity so the file storage can handle UI events for authorization etc.
|
||||
void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState);
|
||||
void OnResume(IFileStorageSetupActivity activity);
|
||||
void OnStart(IFileStorageSetupActivity activity);
|
||||
void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data);
|
||||
|
||||
}
|
||||
|
||||
public interface IWriteTransaction: IDisposable
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using KeePassLib.Serialization;
|
||||
using KeePassLib.Utility;
|
||||
#if !EXCLUDE_JAVAFILESTORAGE
|
||||
@@ -16,6 +18,8 @@ namespace keepass2android.Io
|
||||
#if !EXCLUDE_JAVAFILESTORAGE
|
||||
public abstract class JavaFileStorage: IFileStorage
|
||||
{
|
||||
protected string Protocol { get { return _jfs.ProtocolId; } }
|
||||
|
||||
public IEnumerable<string> SupportedProtocols { get { yield return Protocol; } }
|
||||
|
||||
|
||||
@@ -64,7 +68,7 @@ namespace keepass2android.Io
|
||||
{
|
||||
try
|
||||
{
|
||||
return Jfs.GetCurrentFileVersionFast(ioc.Path);
|
||||
return Jfs.GetCurrentFileVersionFast(IocToPath(ioc));
|
||||
}
|
||||
catch (Java.Lang.Exception e)
|
||||
{
|
||||
@@ -103,55 +107,11 @@ namespace keepass2android.Io
|
||||
return new JavaFileStorageWriteTransaction(IocToPath(ioc), useFileTransaction, this);
|
||||
}
|
||||
|
||||
public IFileStorageSetup RequiredSetup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Jfs.IsConnected)
|
||||
return null;
|
||||
return new JavaFileStorageSetup(this);
|
||||
}
|
||||
}
|
||||
|
||||
internal IJavaFileStorage Jfs
|
||||
{
|
||||
get { return _jfs; }
|
||||
}
|
||||
|
||||
public class JavaFileStorageSetup : IFileStorageSetup, IFileStorageSetupOnResume
|
||||
{
|
||||
private readonly JavaFileStorage _javaFileStorage;
|
||||
|
||||
public JavaFileStorageSetup(JavaFileStorage javaFileStorage)
|
||||
{
|
||||
_javaFileStorage = javaFileStorage;
|
||||
}
|
||||
|
||||
public bool TrySetup(Activity activity)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _javaFileStorage.Jfs.TryConnect(activity);
|
||||
}
|
||||
catch (Java.Lang.Exception e)
|
||||
{
|
||||
throw _javaFileStorage.LogAndConvertJavaException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TrySetupOnResume(Activity activity)
|
||||
{
|
||||
try
|
||||
{
|
||||
_javaFileStorage.Jfs.OnResume();
|
||||
return _javaFileStorage.Jfs.IsConnected;
|
||||
}
|
||||
catch (Java.Lang.Exception e)
|
||||
{
|
||||
throw _javaFileStorage.LogAndConvertJavaException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class JavaFileStorageWriteTransaction: IWriteTransaction
|
||||
{
|
||||
@@ -255,13 +215,14 @@ namespace keepass2android.Io
|
||||
CanWrite = e.CanWrite,
|
||||
IsDirectory = e.IsDirectory,
|
||||
LastModified = JavaTimeToCSharp(e.LastModifiedTime),
|
||||
Path = Protocol + "://" + e.Path,
|
||||
Path = e.Path,
|
||||
SizeInBytes = e.SizeInBytes
|
||||
};
|
||||
}
|
||||
|
||||
public FileDescription GetFileDescription(IOConnectionInfo ioc)
|
||||
{
|
||||
Kp2aLog.Log("GetFileDescription "+ioc.Path);
|
||||
try
|
||||
{
|
||||
return ConvertToFileDescription(Jfs.GetFileEntry(IocToPath(ioc)));
|
||||
@@ -276,24 +237,54 @@ namespace keepass2android.Io
|
||||
}
|
||||
}
|
||||
|
||||
public bool RequiresSetup(IOConnectionInfo ioConnection)
|
||||
{
|
||||
return _jfs.RequiresSetup(IocToPath(ioConnection));
|
||||
}
|
||||
|
||||
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
|
||||
{
|
||||
Kp2aLog.Log("StartSelectFile " + protocolId);
|
||||
_jfs.StartSelectFile((IJavaFileStorageFileStorageSetupInitiatorActivity) activity, isForSave, requestCode);
|
||||
}
|
||||
|
||||
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode)
|
||||
{
|
||||
_jfs.PrepareFileUsage((IJavaFileStorageFileStorageSetupInitiatorActivity)activity, IocToPath(ioc), requestCode);
|
||||
}
|
||||
|
||||
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||
{
|
||||
_jfs.OnCreate(((IJavaFileStorageFileStorageSetupActivity)activity), savedInstanceState);
|
||||
}
|
||||
|
||||
public void OnResume(IFileStorageSetupActivity activity)
|
||||
{
|
||||
Kp2aLog.Log("JFS/OnResume Ioc.Path=" +activity.Ioc.Path+". Path="+((IJavaFileStorageFileStorageSetupActivity)activity).Path);
|
||||
_jfs.OnResume(((IJavaFileStorageFileStorageSetupActivity) activity));
|
||||
}
|
||||
|
||||
public void OnStart(IFileStorageSetupActivity activity)
|
||||
{
|
||||
_jfs.OnStart(((IJavaFileStorageFileStorageSetupActivity) activity));
|
||||
}
|
||||
|
||||
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
_jfs.OnActivityResult(((IJavaFileStorageFileStorageSetupActivity) activity), requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
private DateTime JavaTimeToCSharp(long javatime)
|
||||
{
|
||||
//todo test
|
||||
return new DateTime(1970, 1, 1).AddMilliseconds(javatime);
|
||||
|
||||
}
|
||||
|
||||
private string IocToPath(IOConnectionInfo ioc)
|
||||
public string IocToPath(IOConnectionInfo ioc)
|
||||
{
|
||||
if (ioc.Path.StartsWith(Protocol + "://"))
|
||||
return ioc.Path.Substring(Protocol.Length + 3);
|
||||
else
|
||||
{
|
||||
return ioc.Path;
|
||||
}
|
||||
return ioc.Path;
|
||||
}
|
||||
|
||||
protected abstract string Protocol { get; }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG;INCLUDE_TWOFISH;INCLUDE_KEYBOARD;INCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
@@ -58,7 +58,10 @@
|
||||
<Compile Include="Io\BuiltInFileStorage.cs" />
|
||||
<Compile Include="Io\CachingFileStorage.cs" />
|
||||
<Compile Include="Io\DropboxFileStorage.cs" />
|
||||
<Compile Include="Io\DropboxFileStorageKeys.cs" />
|
||||
<Compile Include="Io\FileDescription.cs" />
|
||||
<Compile Include="Io\FileStorageSetupActivity.cs" />
|
||||
<Compile Include="Io\FileStorageSetupInitiatorActivity.cs" />
|
||||
<Compile Include="Io\GDriveFileStorage.cs" />
|
||||
<Compile Include="Io\IFileStorage.cs" />
|
||||
<Compile Include="Io\IoUtil.cs" />
|
||||
@@ -93,7 +96,7 @@
|
||||
<Compile Include="ProgressDialogStatusLogger.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj" Condition="!$(DefineConstants.Contains('EXCLUDE_JAVAFILESTORAGE'))">
|
||||
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
|
||||
<Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
|
||||
<Name>JavaFileStorageBindings</Name>
|
||||
</ProjectReference>
|
||||
|
||||
Reference in New Issue
Block a user