Add PCloud support.
This commit is contained in:
		| @@ -81,6 +81,9 @@ | ||||
|       <Visible>False</Visible> | ||||
|     </XamarinComponentReference> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <EmbeddedReferenceJar Include="Jars\okhttp-digest-1.7.jar" /> | ||||
|   </ItemGroup> | ||||
| @@ -150,4 +153,4 @@ | ||||
|   <ItemGroup> | ||||
|     <EmbeddedReferenceJar Include="Jars\okio-1.13.0.jar" /> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
| </Project> | ||||
|   | ||||
| @@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.FtpClient.Androi | ||||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamsungPass", "SamsungPass\Xamarin.SamsungPass\SamsungPass\SamsungPass.csproj", "{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}" | ||||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCloudBindings", "PCloudBindings\PCloudBindings.csproj", "{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		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|Win32.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 | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
|   | ||||
							
								
								
									
										23
									
								
								src/Kp2aBusinessLogic/Io/PCloudFileStorage.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/Kp2aBusinessLogic/Io/PCloudFileStorage.cs
									
									
									
									
									
										Normal 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 | ||||
| @@ -117,6 +117,7 @@ | ||||
|     <Compile Include="Io\JavaFileStorage.cs" /> | ||||
|     <Compile Include="Io\NetFtpFileStorage.cs" /> | ||||
|     <Compile Include="Io\OfflineSwitchableFileStorage.cs" /> | ||||
|     <Compile Include="Io\PCloudFileStorage.cs" /> | ||||
|     <Compile Include="Io\SftpFileStorage.cs" /> | ||||
|     <Compile Include="Io\OneDriveFileStorage.cs" /> | ||||
|     <Compile Include="Io\WebDavFileStorage.cs" /> | ||||
| @@ -215,4 +216,4 @@ | ||||
|   <Target Name="AfterBuild"> | ||||
|   </Target> | ||||
|   --> | ||||
| </Project> | ||||
| </Project> | ||||
							
								
								
									
										48
									
								
								src/PCloudBindings/Additions/AboutAdditions.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/PCloudBindings/Additions/AboutAdditions.txt
									
									
									
									
									
										Normal 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; } | ||||
| } | ||||
							
								
								
									
										24
									
								
								src/PCloudBindings/Jars/AboutJars.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/PCloudBindings/Jars/AboutJars.txt
									
									
									
									
									
										Normal 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". | ||||
							
								
								
									
										
											BIN
										
									
								
								src/PCloudBindings/Jars/pcloud-sdk-android-1.0.1.aar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/PCloudBindings/Jars/pcloud-sdk-android-1.0.1.aar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/PCloudBindings/Jars/pcloud-sdk-java-core-1.0.1.jar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/PCloudBindings/Jars/pcloud-sdk-java-core-1.0.1.jar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										75
									
								
								src/PCloudBindings/PCloudBindings.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/PCloudBindings/PCloudBindings.csproj
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										30
									
								
								src/PCloudBindings/Properties/AssemblyInfo.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/PCloudBindings/Properties/AssemblyInfo.cs
									
									
									
									
									
										Normal 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")] | ||||
							
								
								
									
										14
									
								
								src/PCloudBindings/Transforms/EnumFields.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/PCloudBindings/Transforms/EnumFields.xml
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										13
									
								
								src/PCloudBindings/Transforms/EnumMethods.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/PCloudBindings/Transforms/EnumMethods.xml
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										9
									
								
								src/PCloudBindings/Transforms/Metadata.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/PCloudBindings/Transforms/Metadata.xml
									
									
									
									
									
										Normal 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> | ||||
| @@ -35,6 +35,8 @@ dependencies { | ||||
|     compile('com.onedrive.sdk:onedrive-sdk-android:1.2.0') { | ||||
|         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.microsoft.services.msa:msa-auth:0.8.6' | ||||
|     compile 'com.microsoft.aad:adal:1.14.0' | ||||
|   | ||||
| @@ -132,7 +132,7 @@ public class FileEntry { | ||||
| 	 | ||||
| 	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; | ||||
| 	 | ||||
| @@ -157,4 +157,4 @@ public class FileEntry { | ||||
| 	public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data); | ||||
| 	public void onRequestPermissionsResult(FileStorageSetupActivity activity, int requestCode, String[] permissions, int[] grantResults); | ||||
| 	 | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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 | 
| @@ -528,6 +528,7 @@ | ||||
| 	<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="filestoragename_gdrive">Google Drive</string> | ||||
|   <string name="filestoragename_pcloud">PCloud</string> | ||||
|   <string name="filestoragename_onedrive">OneDrive</string> | ||||
|   <string name="filestoragename_sftp">SFTP (SSH File Transfer)</string> | ||||
| 	<string name="filestoragename_content">System file picker</string> | ||||
|   | ||||
| @@ -45,6 +45,7 @@ using KeePassLib.Utility; | ||||
| #if !NoNet | ||||
| using Keepass2android.Javafilestorage; | ||||
| using GoogleDriveFileStorage = keepass2android.Io.GoogleDriveFileStorage; | ||||
| using PCloudFileStorage = keepass2android.Io.PCloudFileStorage; | ||||
| #endif | ||||
| namespace keepass2android | ||||
| { | ||||
| @@ -588,6 +589,7 @@ namespace keepass2android | ||||
| 							new SftpFileStorage(this), | ||||
| 							new NetFtpFileStorage(Application.Context, this), | ||||
| 							new WebDavFileStorage(this), | ||||
| 							new PCloudFileStorage(Application.Context, this), | ||||
| 							//new LegacyWebDavStorage(this), | ||||
| 							//new LegacyFtpStorage(this), | ||||
| #endif | ||||
|   | ||||
| @@ -488,6 +488,7 @@ | ||||
|     <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_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_file.png" /> | ||||
|     <AndroidResource Include="Resources\drawable-mdpi\ic_storage_ftp.png" /> | ||||
| @@ -831,6 +832,10 @@ | ||||
|       <Project>{48574278-4779-4B3A-A9E4-9CF1BC285D0B}</Project> | ||||
|       <Name>JavaFileStorageBindings</Name> | ||||
|     </ProjectReference> | ||||
|     <ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj"> | ||||
|       <Project>{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}</Project> | ||||
|       <Name>PCloudBindings</Name> | ||||
|     </ProjectReference> | ||||
|     <ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj"> | ||||
|       <Project>{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}</Project> | ||||
|       <Name>KeePassLib2Android</Name> | ||||
| @@ -1146,6 +1151,9 @@ | ||||
|   <ItemGroup> | ||||
|     <AndroidResource Include="Resources\drawable-xhdpi\ic_storage_dropboxKP2A.png" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <AndroidResource Include="Resources\drawable-xhdpi\ic_storage_pcloud.png" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <AndroidResource Include="Resources\drawable-xhdpi\ic_storage_file.png" /> | ||||
|   </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.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')" /> | ||||
| </Project> | ||||
| </Project> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Gilbert Gilb's
					Gilbert Gilb's