Add PCloud support.

This commit is contained in:
Gilbert Gilb's
2018-08-27 20:57:30 +02:00
parent 69b63bcb15
commit 5dea97cce2
21 changed files with 672 additions and 5 deletions

View File

@@ -81,6 +81,9 @@
<Visible>False</Visible> <Visible>False</Visible>
</XamarinComponentReference> </XamarinComponentReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedReferenceJar Include="Jars\okhttp-digest-1.7.jar" /> <EmbeddedReferenceJar Include="Jars\okhttp-digest-1.7.jar" />
</ItemGroup> </ItemGroup>
@@ -150,4 +153,4 @@
<ItemGroup> <ItemGroup>
<EmbeddedReferenceJar Include="Jars\okio-1.13.0.jar" /> <EmbeddedReferenceJar Include="Jars\okio-1.13.0.jar" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.FtpClient.Androi
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamsungPass", "SamsungPass\Xamarin.SamsungPass\SamsungPass\SamsungPass.csproj", "{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamsungPass", "SamsungPass\Xamarin.SamsungPass\SamsungPass\SamsungPass.csproj", "{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCloudBindings", "PCloudBindings\PCloudBindings.csproj", "{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -283,6 +285,16 @@ Global
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Debug|Win32.Build.0 = Debug|Any CPU
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Debug|x64.Build.0 = Debug|Any CPU
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Release|Win32.Build.0 = Release|Any CPU
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Release|x64.Build.0 = Release|Any CPU
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Win32.Build.0 = ReleaseNoNet|Any CPU
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|x64.Build.0 = ReleaseNoNet|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -0,0 +1,23 @@
using Android.Content;
#if !EXCLUDE_JAVAFILESTORAGE
namespace keepass2android.Io
{
public partial class PCloudFileStorage: JavaFileStorage
{
private const string ClientId = "CkRWTQXY6Lm";
public PCloudFileStorage(Context ctx, IKp2aApp app) :
base(new Keepass2android.Javafilestorage.PCloudFileStorage(ctx, ClientId), app)
{
}
public override bool UserShouldBackup
{
get { return false; }
}
}
}
#endif

View File

@@ -117,6 +117,7 @@
<Compile Include="Io\JavaFileStorage.cs" /> <Compile Include="Io\JavaFileStorage.cs" />
<Compile Include="Io\NetFtpFileStorage.cs" /> <Compile Include="Io\NetFtpFileStorage.cs" />
<Compile Include="Io\OfflineSwitchableFileStorage.cs" /> <Compile Include="Io\OfflineSwitchableFileStorage.cs" />
<Compile Include="Io\PCloudFileStorage.cs" />
<Compile Include="Io\SftpFileStorage.cs" /> <Compile Include="Io\SftpFileStorage.cs" />
<Compile Include="Io\OneDriveFileStorage.cs" /> <Compile Include="Io\OneDriveFileStorage.cs" />
<Compile Include="Io\WebDavFileStorage.cs" /> <Compile Include="Io\WebDavFileStorage.cs" />
@@ -215,4 +216,4 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>

View File

@@ -0,0 +1,48 @@
Additions allow you to add arbitrary C# to the generated classes
before they are compiled. This can be helpful for providing convenience
methods or adding pure C# classes.
== Adding Methods to Generated Classes ==
Let's say the library being bound has a Rectangle class with a constructor
that takes an x and y position, and a width and length size. It will look like
this:
public partial class Rectangle
{
public Rectangle (int x, int y, int width, int height)
{
// JNI bindings
}
}
Imagine we want to add a constructor to this class that takes a Point and
Size structure instead of 4 ints. We can add a new file called Rectangle.cs
with a partial class containing our new method:
public partial class Rectangle
{
public Rectangle (Point location, Size size) :
this (location.X, location.Y, size.Width, size.Height)
{
}
}
At compile time, the additions class will be added to the generated class
and the final assembly will a Rectangle class with both constructors.
== Adding C# Classes ==
Another thing that can be done is adding fully C# managed classes to the
generated library. In the above example, let's assume that there isn't a
Point class available in Java or our library. The one we create doesn't need
to interact with Java, so we'll create it like a normal class in C#.
By adding a Point.cs file with this class, it will end up in the binding library:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}

View File

@@ -0,0 +1,24 @@
This directory is for Android .jars.
There are 2 types of jars that are supported:
== Input Jar ==
This is the jar that bindings should be generated for.
For example, if you were binding the Google Maps library, this would
be Google's "maps.jar".
Set the build action for these jars in the properties page to "InputJar".
== Reference Jars ==
These are jars that are referenced by the input jar. C# bindings will
not be created for these jars. These jars will be used to resolve
types used by the input jar.
NOTE: Do not add "android.jar" as a reference jar. It will be added automatically
based on the Target Framework selected.
Set the build action for these jars in the properties page to "ReferenceJar".

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}</ProjectGuid>
<ProjectTypeGuids>{6322E8A7-5C46-4E8C-8B19-448B7BC95DC1};{E1126D83-ADAB-4E4F-81F7-4B0A645A68C7}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PCloudBindings</RootNamespace>
<AssemblyName>PCouldBindings</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>0</WarningLevel>
<AndroidLinkMode>None</AndroidLinkMode>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
<OutputPath>bin\ReleaseNoNet\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Jars\AboutJars.txt" />
<None Include="Additions\AboutAdditions.txt" />
<LibraryProjectZip Include="Jars\pcloud-sdk-android-1.0.1.aar" />
</ItemGroup>
<ItemGroup>
<TransformFile Include="Transforms\Metadata.xml" />
<TransformFile Include="Transforms\EnumFields.xml" />
<TransformFile Include="Transforms\EnumMethods.xml" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Bindings.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\pcloud-sdk-java-core-1.0.1.jar" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,30 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("PCloudBindings")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PCloudBindings")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,14 @@
<enum-field-mappings>
<!--
This example converts the constants Fragment_id, Fragment_name,
and Fragment_tag from android.support.v4.app.FragmentActivity.FragmentTag
to an enum called Android.Support.V4.App.FragmentTagType with values
Id, Name, and Tag.
<mapping jni-class="android/support/v4/app/FragmentActivity$FragmentTag" clr-enum-type="Android.Support.V4.App.FragmentTagType">
<field jni-name="Fragment_name" clr-name="Name" value="0" />
<field jni-name="Fragment_id" clr-name="Id" value="1" />
<field jni-name="Fragment_tag" clr-name="Tag" value="2" />
</mapping>
-->
</enum-field-mappings>

View File

@@ -0,0 +1,13 @@
<enum-method-mappings>
<!--
This example changes the Java method:
android.support.v4.app.Fragment.SavedState.writeToParcel (int flags)
to be:
android.support.v4.app.Fragment.SavedState.writeToParcel (Android.OS.ParcelableWriteFlags flags)
when bound in C#.
<mapping jni-class="android/support/v4/app/Fragment.SavedState">
<method jni-name="writeToParcel" parameter="flags" clr-enum-type="Android.OS.ParcelableWriteFlags" />
</mapping>
-->
</enum-method-mappings>

View File

@@ -0,0 +1,9 @@
<metadata>
<!--
This sample removes the class: android.support.v4.content.AsyncTaskLoader.LoadTask:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='AsyncTaskLoader.LoadTask']" />
This sample removes the method: android.support.v4.content.CursorLoader.loadInBackground:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='CursorLoader']/method[@name='loadInBackground']" />
-->
</metadata>

View File

@@ -35,6 +35,8 @@ dependencies {
compile('com.onedrive.sdk:onedrive-sdk-android:1.2.0') { compile('com.onedrive.sdk:onedrive-sdk-android:1.2.0') {
transitive = false transitive = false
} }
compile 'com.pcloud.sdk:java-core:1.0.1'
compile 'com.pcloud.sdk:android:1.0.1'
compile 'com.google.code.gson:gson:2.3.1' compile 'com.google.code.gson:gson:2.3.1'
compile 'com.microsoft.services.msa:msa-auth:0.8.6' compile 'com.microsoft.services.msa:msa-auth:0.8.6'
compile 'com.microsoft.aad:adal:1.14.0' compile 'com.microsoft.aad:adal:1.14.0'

View File

@@ -132,7 +132,7 @@ public class FileEntry {
public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception; public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception;
public String getCurrentFileVersionFast(String path); public String getCurrentFileVersionFast(String path) throws Exception;
public InputStream openFileForRead(String path) throws Exception; public InputStream openFileForRead(String path) throws Exception;
@@ -157,4 +157,4 @@ public class FileEntry {
public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data); public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data);
public void onRequestPermissionsResult(FileStorageSetupActivity activity, int requestCode, String[] permissions, int[] grantResults); public void onRequestPermissionsResult(FileStorageSetupActivity activity, int requestCode, String[] permissions, int[] grantResults);
} }

View File

@@ -0,0 +1,402 @@
package keepass2android.javafilestorage;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.regex.Pattern;
import com.pcloud.sdk.ApiClient;
import com.pcloud.sdk.ApiError;
import com.pcloud.sdk.Authenticators;
import com.pcloud.sdk.AuthorizationActivity;
import com.pcloud.sdk.AuthorizationResult;
import com.pcloud.sdk.Call;
import com.pcloud.sdk.DataSource;
import com.pcloud.sdk.PCloudSdk;
import com.pcloud.sdk.RemoteEntry;
import com.pcloud.sdk.RemoteFile;
import com.pcloud.sdk.RemoteFolder;
/**
* FileStorage implementation for PCloud provider.
* https://www.pcloud.com/
*/
public class PCloudFileStorage extends JavaFileStorageBase
{
final static private int PCLOUD_AUTHORIZATION_REQUEST_CODE = 1001845497;
final static private String SHARED_PREF_NAME = "PCLOUD";
final static private String SHARED_PREF_AUTH_TOKEN = "AUTH_TOKEN";
private final Context ctx;
private ApiClient apiClient;
private String clientId;
public PCloudFileStorage(Context ctx, String clientId) {
this.ctx = ctx;
this.clientId = clientId;
this.apiClient = createApiClientFromSharedPrefs();
}
@Override
public boolean requiresSetup(String path) {
return true;
}
@Override
public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave, int requestCode) {
String path = getProtocolId() + "://";
activity.startSelectFileProcess(path, isForSave, requestCode);
}
@Override
public void prepareFileUsage(Context appContext, String path) throws Throwable {
if (!isConnected()) {
throw new UserInteractionRequiredException();
}
}
@Override
public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) {
if (isConnected()) {
Intent intent = new Intent();
intent.putExtra(EXTRA_PATH, path);
activity.onImmediateResult(requestCode, RESULT_FILEUSAGE_PREPARED, intent);
} else {
activity.startFileUsageProcess(path, requestCode, alwaysReturnSuccess);
}
}
@Override
public String getProtocolId() {
return "pcloud";
}
@Override
public String getDisplayName(String path) {
return path;
}
@Override
public String getFilename(String path) {
return path.substring(path.lastIndexOf("/") + 1);
}
@Override
public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception {
if (previousFileVersion == null || "".equals(previousFileVersion)) {
return false;
}
path = this.cleanPath(path);
RemoteFile remoteFile = this.getRemoteFileByPath(path);
return !remoteFile.hash().equals(previousFileVersion);
}
@Override
public String getCurrentFileVersionFast(String path) throws Exception {
path = this.cleanPath(path);
RemoteFile remoteFile = this.getRemoteFileByPath(path);
return remoteFile.hash();
}
@Override
public InputStream openFileForRead(String path) throws Exception {
path = this.cleanPath(path);
RemoteFile remoteFile = this.getRemoteFileByPath(path);
return remoteFile.byteStream();
}
@Override
public void uploadFile(String path, byte[] data, boolean writeTransactional) throws Exception {
path = this.cleanPath(path);
DataSource dataSource = DataSource.create(data);
String filename = path.substring(path.lastIndexOf("/") + 1);
String filePath = path.substring(0, path.lastIndexOf("/") + 1);
RemoteFolder remoteFolder = this.getRemoteFolderByPath(filePath);
try {
this.apiClient.createFile(remoteFolder, filename, dataSource).execute();
} catch (ApiError e) {
throw convertApiError(e);
}
}
@Override
public String createFolder(String parentPath, String newDirName) throws Exception {
String parentPathWithoutProtocol = this.cleanPath(parentPath);
RemoteFolder remoteFolder = this.getRemoteFolderByPath(parentPathWithoutProtocol);
try {
this.apiClient.createFolder(remoteFolder, newDirName).execute();
} catch (ApiError e) {
throw convertApiError(e);
}
return this.createFilePath(parentPath, newDirName);
}
@Override
public String createFilePath(String parentPath, String newFileName) throws Exception {
return (
this.getProtocolId() + "://" +
this.cleanPath(parentPath) +
("".equals(newFileName) ? "" : "/") + newFileName
);
}
@Override
public List<FileEntry> listFiles(String parentPath) throws Exception {
parentPath = this.cleanPath(parentPath);
ArrayList<FileEntry> fileEntries = new ArrayList<>();
RemoteFolder remoteFolder = this.getRemoteFolderByPath(parentPath);
for (RemoteEntry remoteEntry : remoteFolder.children()) {
fileEntries.add(this.convertRemoteEntryToFileEntry(remoteEntry, parentPath));
}
return fileEntries;
}
@Override
public FileEntry getFileEntry(String path) throws Exception {
path = this.cleanPath(path);
RemoteEntry remoteEntry = this.getRemoteEntryByPath(path);
return this.convertRemoteEntryToFileEntry(
remoteEntry,
path.substring(0, path.lastIndexOf("/"))
);
}
@Override
public void delete(String path) throws Exception {
path = this.cleanPath(path);
RemoteEntry remoteEntry = this.getRemoteFileByPath(path);
try {
this.apiClient.delete(remoteEntry).execute();
} catch (ApiError e) {
throw convertApiError(e);
}
}
@Override
public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState) {
}
@Override
public void onResume(FileStorageSetupActivity activity) {
if (activity.getProcessName().equals(PROCESS_NAME_SELECTFILE)) {
activity.getState().putString(EXTRA_PATH, activity.getPath());
}
if (this.isConnected()) {
finishActivityWithSuccess(activity);
} else if (!activity.getState().getBoolean("hasStartedAuth", false)) {
Activity castedActivity = (Activity)activity;
Intent authIntent = AuthorizationActivity.createIntent(castedActivity, this.clientId);
castedActivity.startActivityForResult(authIntent, PCLOUD_AUTHORIZATION_REQUEST_CODE);
activity.getState().putBoolean("hasStartedAuth", true);
}
}
@Override
public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) {
if (requestCode == PCLOUD_AUTHORIZATION_REQUEST_CODE && data != null) {
activity.getState().putBoolean("hasStartedAuth", false);
AuthorizationResult result = (AuthorizationResult)(
data.getSerializableExtra(AuthorizationActivity.KEY_AUTHORIZATION_RESULT)
);
this.handleAuthResult(activity, result, data);
}
}
private void handleAuthResult(FileStorageSetupActivity activity, AuthorizationResult authorizationResult,
Intent data) {
if (authorizationResult == AuthorizationResult.ACCESS_GRANTED) {
String authToken = data.getStringExtra(AuthorizationActivity.KEY_ACCESS_TOKEN);
setAuthToken(authToken);
finishActivityWithSuccess(activity);
} else {
Activity castedActivity = (Activity)activity;
Intent resultData = new Intent();
resultData.putExtra(EXTRA_ERROR_MESSAGE, "Authentication failed.");
castedActivity.setResult(Activity.RESULT_CANCELED, resultData);
castedActivity.finish();
}
}
@Override
public void onStart(FileStorageSetupActivity activity) {
}
private ApiClient createApiClientFromSharedPrefs() {
SharedPreferences prefs = this.ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
String authToken = prefs.getString(SHARED_PREF_AUTH_TOKEN, null);
return this.createApiClient(authToken);
}
private ApiClient createApiClient(String authToken) {
if (authToken == null) {
return null;
}
return (
PCloudSdk.newClientBuilder()
.authenticator(Authenticators.newOAuthAuthenticator(authToken))
.create()
);
}
private boolean isConnected() {
return (this.apiClient != null);
}
private void clearAuthToken() {
this.apiClient = null;
SharedPreferences prefs = this.ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor edit = prefs.edit();
edit.clear();
edit.apply();
}
private void setAuthToken(String authToken) {
this.apiClient = this.createApiClient(authToken);
SharedPreferences prefs = this.ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor edit = prefs.edit();
edit.putString(SHARED_PREF_AUTH_TOKEN, authToken);
edit.apply();
}
private String cleanPath(String path) {
return (
"/" + path.replaceAll("^(" + Pattern.quote(this.getProtocolId()) + "://)?/*", "")
);
}
private RemoteFile getRemoteFileByPath(String path) throws Exception {
RemoteEntry remoteEntry = this.getRemoteEntryByPath(path);
try {
return remoteEntry.asFile();
} catch (IllegalStateException e) {
throw new FileNotFoundException(e.toString());
}
}
private RemoteFolder getRemoteFolderByPath(String path) throws Exception {
RemoteEntry remoteEntry = this.getRemoteEntryByPath(path);
try {
return remoteEntry.asFolder();
} catch (IllegalStateException e) {
throw new FileNotFoundException(e.toString());
}
}
private RemoteEntry getRemoteEntryByPath(String path) throws Exception {
Call<RemoteFolder> call = this.apiClient.listFolder(RemoteFolder.ROOT_FOLDER_ID, true);
RemoteFolder folder;
try {
folder = call.execute();
} catch (ApiError apiError) {
throw convertApiError(apiError);
}
if ("/".equals(path)) {
return folder;
}
String[] fileNames = path.substring(1).split("/");
RemoteFolder currentFolder = folder;
Iterator<String> fileNamesIterator = Arrays.asList(fileNames).iterator();
while (true) {
String fileName = fileNamesIterator.next();
Iterator<RemoteEntry> entryIterator = currentFolder.children().iterator();
while (true) {
RemoteEntry remoteEntry;
try {
remoteEntry = entryIterator.next();
} catch (NoSuchElementException e) {
throw new FileNotFoundException(e.toString());
}
if (currentFolder.folderId() == remoteEntry.parentFolderId() && fileName.equals(remoteEntry.name())) {
if (!fileNamesIterator.hasNext()) {
return remoteEntry;
}
try {
currentFolder = remoteEntry.asFolder();
} catch (IllegalStateException e) {
throw new FileNotFoundException(e.toString());
}
break;
}
}
}
}
private Exception convertApiError(ApiError e) {
String strErrorCode = String.valueOf(e.errorCode());
if (strErrorCode.startsWith("1") || "2000".equals(strErrorCode)) {
this.clearAuthToken();
return new UserInteractionRequiredException("Unlinked from PCloud! User must re-link.", e);
} else if (strErrorCode.startsWith("2")) {
return new FileNotFoundException(e.toString());
}
return e;
}
private FileEntry convertRemoteEntryToFileEntry(RemoteEntry remoteEntry, String parentPath) {
FileEntry fileEntry = new FileEntry();
fileEntry.canRead = true;
fileEntry.canWrite = true;
fileEntry.path = (
this.getProtocolId() + "://" +
("/".equals(parentPath) ? "" : parentPath) +
"/" + remoteEntry.name()
);
fileEntry.displayName = remoteEntry.name();
fileEntry.isDirectory = !remoteEntry.isFile();
fileEntry.lastModifiedTime = remoteEntry.lastModified().getTime();
if (remoteEntry.isFile()) {
fileEntry.sizeInBytes = remoteEntry.asFile().size();
}
return fileEntry;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -528,6 +528,7 @@
<string name="filestoragename_dropboxKP2A">Dropbox (KP2A folder)</string> <string name="filestoragename_dropboxKP2A">Dropbox (KP2A folder)</string>
<string name="filestoragehelp_dropboxKP2A">If you do not want to give KP2A access to your full Dropbox, you may select this option. It will request only access to the folder Apps/Keepass2Android. This is especially suited when creating a new database. If you already have a database, click this option to create the folder, then place your file inside the folder (from your PC) and then select this option again for opening the file.</string> <string name="filestoragehelp_dropboxKP2A">If you do not want to give KP2A access to your full Dropbox, you may select this option. It will request only access to the folder Apps/Keepass2Android. This is especially suited when creating a new database. If you already have a database, click this option to create the folder, then place your file inside the folder (from your PC) and then select this option again for opening the file.</string>
<string name="filestoragename_gdrive">Google Drive</string> <string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragename_onedrive">OneDrive</string> <string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_sftp">SFTP (SSH File Transfer)</string> <string name="filestoragename_sftp">SFTP (SSH File Transfer)</string>
<string name="filestoragename_content">System file picker</string> <string name="filestoragename_content">System file picker</string>

View File

@@ -45,6 +45,7 @@ using KeePassLib.Utility;
#if !NoNet #if !NoNet
using Keepass2android.Javafilestorage; using Keepass2android.Javafilestorage;
using GoogleDriveFileStorage = keepass2android.Io.GoogleDriveFileStorage; using GoogleDriveFileStorage = keepass2android.Io.GoogleDriveFileStorage;
using PCloudFileStorage = keepass2android.Io.PCloudFileStorage;
#endif #endif
namespace keepass2android namespace keepass2android
{ {
@@ -588,6 +589,7 @@ namespace keepass2android
new SftpFileStorage(this), new SftpFileStorage(this),
new NetFtpFileStorage(Application.Context, this), new NetFtpFileStorage(Application.Context, this),
new WebDavFileStorage(this), new WebDavFileStorage(this),
new PCloudFileStorage(Application.Context, this),
//new LegacyWebDavStorage(this), //new LegacyWebDavStorage(this),
//new LegacyFtpStorage(this), //new LegacyFtpStorage(this),
#endif #endif

View File

@@ -488,6 +488,7 @@
<AndroidResource Include="Resources\drawable-mdpi\ic_storage_androidsend.png" /> <AndroidResource Include="Resources\drawable-mdpi\ic_storage_androidsend.png" />
<AndroidResource Include="Resources\drawable-mdpi\ic_storage_content.png" /> <AndroidResource Include="Resources\drawable-mdpi\ic_storage_content.png" />
<AndroidResource Include="Resources\drawable-mdpi\ic_storage_dropbox.png" /> <AndroidResource Include="Resources\drawable-mdpi\ic_storage_dropbox.png" />
<AndroidResource Include="Resources\drawable-mdpi\ic_storage_pcloud.png" />
<AndroidResource Include="Resources\drawable-mdpi\ic_storage_dropboxKP2A.png" /> <AndroidResource Include="Resources\drawable-mdpi\ic_storage_dropboxKP2A.png" />
<AndroidResource Include="Resources\drawable-mdpi\ic_storage_file.png" /> <AndroidResource Include="Resources\drawable-mdpi\ic_storage_file.png" />
<AndroidResource Include="Resources\drawable-mdpi\ic_storage_ftp.png" /> <AndroidResource Include="Resources\drawable-mdpi\ic_storage_ftp.png" />
@@ -831,6 +832,10 @@
<Project>{48574278-4779-4B3A-A9E4-9CF1BC285D0B}</Project> <Project>{48574278-4779-4B3A-A9E4-9CF1BC285D0B}</Project>
<Name>JavaFileStorageBindings</Name> <Name>JavaFileStorageBindings</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj">
<Project>{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}</Project>
<Name>PCloudBindings</Name>
</ProjectReference>
<ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj"> <ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj">
<Project>{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}</Project> <Project>{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}</Project>
<Name>KeePassLib2Android</Name> <Name>KeePassLib2Android</Name>
@@ -1146,6 +1151,9 @@
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\ic_storage_dropboxKP2A.png" /> <AndroidResource Include="Resources\drawable-xhdpi\ic_storage_dropboxKP2A.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\ic_storage_pcloud.png" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\ic_storage_file.png" /> <AndroidResource Include="Resources\drawable-xhdpi\ic_storage_file.png" />
</ItemGroup> </ItemGroup>
@@ -1909,4 +1917,4 @@
<Import Project="..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Animated.Vector.Drawable.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Animated.Vector.Drawable.targets')" /> <Import Project="..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Animated.Vector.Drawable.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Animated.Vector.Drawable.targets')" />
<Import Project="..\packages\Xamarin.Android.Support.v7.AppCompat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.v7.AppCompat.targets" Condition="Exists('..\packages\Xamarin.Android.Support.v7.AppCompat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.v7.AppCompat.targets')" /> <Import Project="..\packages\Xamarin.Android.Support.v7.AppCompat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.v7.AppCompat.targets" Condition="Exists('..\packages\Xamarin.Android.Support.v7.AppCompat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.v7.AppCompat.targets')" />
<Import Project="..\packages\Xamarin.Android.Support.Design.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Design.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Design.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Design.targets')" /> <Import Project="..\packages\Xamarin.Android.Support.Design.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Design.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Design.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Design.targets')" />
</Project> </Project>