mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-11-25 10:42:17 -05:00
Merge branch 'master' of https://git01.codeplex.com/keepass2android into HEAD
Conflicts: src/java/JavaFileStorage/bin/javafilestorage.jar
This commit is contained in:
parent
e77c8f9ce5
commit
82c8b124bd
2
.gitignore
vendored
2
.gitignore
vendored
@ -282,3 +282,5 @@ Thumbs.db
|
|||||||
/src/java/MasterKee
|
/src/java/MasterKee
|
||||||
|
|
||||||
/src/PluginSdkBinding/obj/ReleaseNoNet
|
/src/PluginSdkBinding/obj/ReleaseNoNet
|
||||||
|
/src/MasterKeeWinPlugin/bin/Release
|
||||||
|
/src/SamplePlugin
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>8.0.30703</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{3C0F7FE5-639F-4422-A087-8B26CF862D1B}</ProjectGuid>
|
<ProjectGuid>{3C0F7FE5-639F-4422-A087-8B26CF862D1B}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -22,6 +20,7 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@ -30,6 +29,7 @@
|
|||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
||||||
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
||||||
@ -39,30 +39,30 @@
|
|||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="Xamarin.Android.Support.v4">
|
||||||
|
<HintPath>..\packages\Xamarin.Android.Support.v4.20.0.0.4\lib\MonoAndroid10\Xamarin.Android.Support.v4.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<LibraryProjectZip Include="..\java\android-filechooser\code\project.zip">
|
|
||||||
<Link>project.zip</Link>
|
|
||||||
</LibraryProjectZip>
|
|
||||||
<None Include="Jars\AboutJars.txt" />
|
<None Include="Jars\AboutJars.txt" />
|
||||||
<None Include="Additions\AboutAdditions.txt" />
|
<None Include="Additions\AboutAdditions.txt" />
|
||||||
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<TransformFile Include="Transforms\Metadata.xml" />
|
<TransformFile Include="Transforms\Metadata.xml" />
|
||||||
<TransformFile Include="Transforms\EnumFields.xml" />
|
<TransformFile Include="Transforms\EnumFields.xml" />
|
||||||
<TransformFile Include="Transforms\EnumMethods.xml" />
|
<TransformFile Include="Transforms\EnumMethods.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedReferenceJar Include="Jars\android-support-v4.jar" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Bindings.targets" />
|
<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.
|
<!-- 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.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
@ -71,4 +71,15 @@
|
|||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
-->
|
||||||
|
<ItemGroup>
|
||||||
|
<LibraryProjectZip Include="..\java\android-filechooser\code\project.zip">
|
||||||
|
<Link>project.zip</Link>
|
||||||
|
</LibraryProjectZip>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<XamarinComponentReference Include="xamandroidsupportv4-18">
|
||||||
|
<Version>20.0.0.4</Version>
|
||||||
|
<Visible>False</Visible>
|
||||||
|
</XamarinComponentReference>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
Binary file not shown.
@ -1,5 +1,8 @@
|
|||||||
<metadata>
|
<metadata>
|
||||||
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser.providers.localfile']/class[@name='FileObserverEx']" />
|
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser.providers.localfile']/class[@name='FileObserverEx']" />
|
||||||
|
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser']/class[@name='FragmentFiles']" />
|
||||||
|
<remove-node path="/api/package[@name='group.pals.android.lib.ui.filechooser.prefs']" />
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
|
4
src/AndroidFileChooserBinding/packages.config
Normal file
4
src/AndroidFileChooserBinding/packages.config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Xamarin.Android.Support.v4" version="20.0.0.4" targetFramework="MonoAndroid22" />
|
||||||
|
</packages>
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>8.0.30703</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{48574278-4779-4B3A-A9E4-9CF1BC285D0B}</ProjectGuid>
|
<ProjectGuid>{48574278-4779-4B3A-A9E4-9CF1BC285D0B}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -22,6 +20,7 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@ -30,6 +29,7 @@
|
|||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
||||||
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
||||||
@ -39,14 +39,25 @@
|
|||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="GooglePlayServicesFroyoLib">
|
|
||||||
<HintPath>..\Components\googleplayservicesfroyo-9.0\lib\android\GooglePlayServicesFroyoLib.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="GooglePlayServicesLib">
|
||||||
|
<HintPath>..\..\..\Components\googleplayservices-19.0.0\lib\android\GooglePlayServicesLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xamarin.Android.Support.v4">
|
||||||
|
<HintPath>..\..\..\Components\googleplayservices-19.0.0\lib\android\Xamarin.Android.Support.v4.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xamarin.Android.Support.v7.AppCompat">
|
||||||
|
<HintPath>..\..\..\Components\googleplayservices-19.0.0\lib\android\Xamarin.Android.Support.v7.AppCompat.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xamarin.Android.Support.v7.MediaRouter">
|
||||||
|
<HintPath>..\..\..\Components\googleplayservices-19.0.0\lib\android\Xamarin.Android.Support.v7.MediaRouter.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
@ -65,11 +76,6 @@
|
|||||||
<Link>Jars\javafilestorage.jar</Link>
|
<Link>Jars\javafilestorage.jar</Link>
|
||||||
</EmbeddedJar>
|
</EmbeddedJar>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedReferenceJar Include="..\java\JavaFileStorage\libs\android-support-v4.jar">
|
|
||||||
<Link>Jars\android-support-v4.jar</Link>
|
|
||||||
</EmbeddedReferenceJar>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedReferenceJar Include="..\java\JavaFileStorage\libs\dropbox-android-sdk-1.5.4.jar">
|
<EmbeddedReferenceJar Include="..\java\JavaFileStorage\libs\dropbox-android-sdk-1.5.4.jar">
|
||||||
<Link>Jars\dropbox-android-sdk-1.5.4.jar</Link>
|
<Link>Jars\dropbox-android-sdk-1.5.4.jar</Link>
|
||||||
@ -158,4 +164,10 @@
|
|||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
-->
|
||||||
|
<ItemGroup>
|
||||||
|
<XamarinComponentReference Include="googleplayservices">
|
||||||
|
<Version>19.0.0</Version>
|
||||||
|
<Visible>False</Visible>
|
||||||
|
</XamarinComponentReference>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>8.0.30703</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{70D3844A-D9FA-4A64-B205-A84C6A822196}</ProjectGuid>
|
<ProjectGuid>{70D3844A-D9FA-4A64-B205-A84C6A822196}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -22,6 +20,7 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@ -30,6 +29,7 @@
|
|||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
||||||
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
||||||
@ -39,6 +39,8 @@
|
|||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>10.0.0</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}</ProjectGuid>
|
<ProjectGuid>{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -20,7 +18,7 @@
|
|||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>False</Optimize>
|
<Optimize>False</Optimize>
|
||||||
<OutputPath>bin\Debug</OutputPath>
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;INCLUDE_KEYBOARD;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE;EXCLUDE_KEYTRANSFORM</DefineConstants>
|
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;INCLUDE_FILECHOOSER;_EXCLUDE_JAVAFILESTORAGE;INCLUDE_KEYTRANSFORM</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<ConsolePause>False</ConsolePause>
|
<ConsolePause>False</ConsolePause>
|
||||||
@ -34,7 +32,6 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||||
<ConsolePause>False</ConsolePause>
|
<ConsolePause>False</ConsolePause>
|
||||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU' ">
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
@ -44,7 +41,6 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||||
<ConsolePause>False</ConsolePause>
|
<ConsolePause>False</ConsolePause>
|
||||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="protobuf-net">
|
<Reference Include="protobuf-net">
|
||||||
@ -156,7 +152,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
|
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
|
||||||
<Project>{70d3844a-d9fa-4a64-b205-a84c6a822196}</Project>
|
<Project>{70D3844A-D9FA-4A64-B205-A84C6A822196}</Project>
|
||||||
<Name>KP2AKdbLibraryBinding</Name>
|
<Name>KP2AKdbLibraryBinding</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -41,15 +41,16 @@ namespace KeePassLib.Keys
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class KcpKeyFile : IUserKey
|
public sealed class KcpKeyFile : IUserKey
|
||||||
{
|
{
|
||||||
private string m_strPath;
|
private IOConnectionInfo m_ioc;
|
||||||
private ProtectedBinary m_pbKeyData;
|
private ProtectedBinary m_pbKeyData;
|
||||||
|
private ProtectedBinary m_pbFileData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path to the key file.
|
/// Path to the key file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Path
|
public string Path
|
||||||
{
|
{
|
||||||
get { return m_strPath; }
|
get { return m_ioc.Path; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -62,6 +63,16 @@ namespace KeePassLib.Keys
|
|||||||
get { return m_pbKeyData; }
|
get { return m_pbKeyData; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo Ioc
|
||||||
|
{
|
||||||
|
get { return m_ioc; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProtectedBinary RawFileData
|
||||||
|
{
|
||||||
|
get { return m_pbFileData; }
|
||||||
|
}
|
||||||
|
|
||||||
public KcpKeyFile(string strKeyFile)
|
public KcpKeyFile(string strKeyFile)
|
||||||
{
|
{
|
||||||
Construct(IOConnectionInfo.FromPath(strKeyFile), false);
|
Construct(IOConnectionInfo.FromPath(strKeyFile), false);
|
||||||
@ -82,17 +93,22 @@ namespace KeePassLib.Keys
|
|||||||
Construct(iocKeyFile, bThrowIfDbFile);
|
Construct(iocKeyFile, bThrowIfDbFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Construct(IOConnectionInfo iocFile, bool bThrowIfDbFile)
|
public KcpKeyFile(byte[] keyFileContents, IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
|
||||||
{
|
{
|
||||||
byte[] pbFileData = IOConnection.ReadFile(iocFile);
|
Construct(keyFileContents, iocKeyFile, bThrowIfDbFile);
|
||||||
if(pbFileData == null) throw new Java.IO.FileNotFoundException();
|
}
|
||||||
|
|
||||||
if(bThrowIfDbFile && (pbFileData.Length >= 8))
|
private void Construct(byte[] pbFileData, IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
|
||||||
|
{
|
||||||
|
if (pbFileData == null) throw new Java.IO.FileNotFoundException();
|
||||||
|
m_pbFileData = new ProtectedBinary(true, pbFileData);
|
||||||
|
|
||||||
|
if (bThrowIfDbFile && (pbFileData.Length >= 8))
|
||||||
{
|
{
|
||||||
uint uSig1 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 0, 4));
|
uint uSig1 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 0, 4));
|
||||||
uint uSig2 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 4, 4));
|
uint uSig2 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 4, 4));
|
||||||
|
|
||||||
if(((uSig1 == KdbxFile.FileSignature1) &&
|
if (((uSig1 == KdbxFile.FileSignature1) &&
|
||||||
(uSig2 == KdbxFile.FileSignature2)) ||
|
(uSig2 == KdbxFile.FileSignature2)) ||
|
||||||
((uSig1 == KdbxFile.FileSignaturePreRelease1) &&
|
((uSig1 == KdbxFile.FileSignaturePreRelease1) &&
|
||||||
(uSig2 == KdbxFile.FileSignaturePreRelease2)) ||
|
(uSig2 == KdbxFile.FileSignaturePreRelease2)) ||
|
||||||
@ -106,16 +122,22 @@ namespace KeePassLib.Keys
|
|||||||
}
|
}
|
||||||
|
|
||||||
byte[] pbKey = LoadXmlKeyFile(pbFileData);
|
byte[] pbKey = LoadXmlKeyFile(pbFileData);
|
||||||
if(pbKey == null) pbKey = LoadKeyFile(pbFileData);
|
if (pbKey == null) pbKey = LoadKeyFile(pbFileData);
|
||||||
|
|
||||||
if(pbKey == null) throw new InvalidOperationException();
|
if (pbKey == null) throw new InvalidOperationException();
|
||||||
|
|
||||||
m_strPath = iocFile.Path;
|
m_ioc = iocKeyFile;
|
||||||
m_pbKeyData = new ProtectedBinary(true, pbKey);
|
m_pbKeyData = new ProtectedBinary(true, pbKey);
|
||||||
|
|
||||||
MemUtil.ZeroByteArray(pbKey);
|
MemUtil.ZeroByteArray(pbKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Construct(IOConnectionInfo iocFile, bool bThrowIfDbFile)
|
||||||
|
{
|
||||||
|
byte[] pbFileData = IOConnection.ReadFile(iocFile);
|
||||||
|
Construct(pbFileData, iocFile, bThrowIfDbFile);
|
||||||
|
}
|
||||||
|
|
||||||
// public void Clear()
|
// public void Clear()
|
||||||
// {
|
// {
|
||||||
// m_strPath = string.Empty;
|
// m_strPath = string.Empty;
|
||||||
|
@ -647,14 +647,17 @@ namespace KeePassLib.Serialization
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
sIn = IOConnection.OpenRead(ioc);
|
sIn = IOConnection.OpenRead(ioc);
|
||||||
if(sIn == null) return null;
|
if (sIn == null) return null;
|
||||||
|
|
||||||
ms = new MemoryStream();
|
ms = new MemoryStream();
|
||||||
MemUtil.CopyStream(sIn, ms);
|
MemUtil.CopyStream(sIn, ms);
|
||||||
|
|
||||||
return ms.ToArray();
|
return ms.ToArray();
|
||||||
}
|
}
|
||||||
catch(Exception) { }
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Kp2aLog.Log("error opening file: " + e);
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if(sIn != null) sIn.Close();
|
if(sIn != null) sIn.Close();
|
||||||
|
@ -79,8 +79,17 @@ namespace keepass2android
|
|||||||
Handler UiThreadHandler { get; }
|
Handler UiThreadHandler { get; }
|
||||||
|
|
||||||
IProgressDialog CreateProgressDialog(Context ctx);
|
IProgressDialog CreateProgressDialog(Context ctx);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// returns the file storage for the given ioc. might be a caching file storage
|
||||||
|
/// </summary>
|
||||||
IFileStorage GetFileStorage(IOConnectionInfo iocInfo);
|
IFileStorage GetFileStorage(IOConnectionInfo iocInfo);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// returns the file storage for the given ioc. if allowCache=false, no cached file storage is returned
|
||||||
|
/// </summary>
|
||||||
|
IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache);
|
||||||
|
|
||||||
void TriggerReload(Context context);
|
void TriggerReload(Context context);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -90,5 +99,7 @@ namespace keepass2android
|
|||||||
//bool OnServerCertificateError(int certificateProblem);
|
//bool OnServerCertificateError(int certificateProblem);
|
||||||
|
|
||||||
RemoteCertificateValidationCallback CertificateValidationCallback { get; }
|
RemoteCertificateValidationCallback CertificateValidationCallback { get; }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
200
src/Kp2aBusinessLogic/Io/AndroidContentStorage.cs
Normal file
200
src/Kp2aBusinessLogic/Io/AndroidContentStorage.cs
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
|
using Android.Runtime;
|
||||||
|
using Android.Views;
|
||||||
|
using Android.Widget;
|
||||||
|
using KeePassLib.Serialization;
|
||||||
|
|
||||||
|
namespace keepass2android.Io
|
||||||
|
{
|
||||||
|
//TODOC,TOTEST, TODO: unimplemented methods?
|
||||||
|
public class AndroidContentStorage: IFileStorage
|
||||||
|
{
|
||||||
|
private readonly Context _ctx;
|
||||||
|
|
||||||
|
public AndroidContentStorage(Context ctx)
|
||||||
|
{
|
||||||
|
_ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> SupportedProtocols
|
||||||
|
{
|
||||||
|
get { yield return "content"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream OpenFileForRead(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return _ctx.ContentResolver.OpenInputStream(Android.Net.Uri.Parse(ioc.Path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||||
|
{
|
||||||
|
return new AndroidContentWriteTransaction(ioc.Path, _ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RequiresCredentials(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileDescription GetFileDescription(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RequiresSetup(IOConnectionInfo ioConnection)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string IocToPath(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return ioc.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
activity.IocToIntent(intent, new IOConnectionInfo() { Path = protocolId + "://" });
|
||||||
|
activity.OnImmediateResult(requestCode, (int)FileStorageResults.FileChooserPrepared, intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
|
||||||
|
bool alwaysReturnSuccess)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
activity.IocToIntent(intent, ioc);
|
||||||
|
activity.OnImmediateResult(requestCode, (int)FileStorageResults.FileUsagePrepared, intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnResume(IFileStorageSetupActivity activity)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnStart(IFileStorageSetupActivity activity)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetDisplayName(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return ioc.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateFilePath(string parent, string newFilename)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
//on pre-Kitkat devices, content:// is always temporary:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
//on pre-Kitkat devices, we can't write content:// files
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AndroidContentWriteTransaction : IWriteTransaction
|
||||||
|
{
|
||||||
|
private readonly string _path;
|
||||||
|
private readonly Context _ctx;
|
||||||
|
private MemoryStream _memoryStream;
|
||||||
|
|
||||||
|
public AndroidContentWriteTransaction(string path, Context ctx)
|
||||||
|
{
|
||||||
|
_path = path;
|
||||||
|
_ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_memoryStream.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream OpenFile()
|
||||||
|
{
|
||||||
|
_memoryStream = new MemoryStream();
|
||||||
|
return _memoryStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CommitWrite()
|
||||||
|
{
|
||||||
|
using (Stream outputStream = _ctx.ContentResolver.OpenOutputStream(Android.Net.Uri.Parse(_path)))
|
||||||
|
{
|
||||||
|
outputStream.Write(_memoryStream.ToArray(), 0, (int)_memoryStream.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -4,6 +4,7 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
|
using System.Security;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Java.Security.Cert;
|
using Java.Security.Cert;
|
||||||
@ -295,5 +296,67 @@ namespace keepass2android.Io
|
|||||||
res.Path += filename;
|
res.Path += filename;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnlyBecauseKitkatRestrictions(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
if (IsLocalFileFlaggedReadOnly(ioc))
|
||||||
|
return false; //it's not read-only because of the restrictions introduced in kitkat
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//test if we can open
|
||||||
|
//http://www.doubleencore.com/2014/03/android-external-storage/#comment-1294469517
|
||||||
|
using (var writer = new Java.IO.FileOutputStream(ioc.Path, true))
|
||||||
|
{
|
||||||
|
writer.Close();
|
||||||
|
return false; //we can write
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Java.IO.IOException)
|
||||||
|
{
|
||||||
|
//seems like we can't write to that location even though it's not read-only
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
if (ioc.IsLocalFile())
|
||||||
|
{
|
||||||
|
if (IsLocalFileFlaggedReadOnly(ioc))
|
||||||
|
return true;
|
||||||
|
if (IsReadOnlyBecauseKitkatRestrictions(ioc))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//for remote files assume they can be written: (think positive! :-) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsLocalFileFlaggedReadOnly(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new FileInfo(ioc.Path).IsReadOnly;
|
||||||
|
}
|
||||||
|
catch (SecurityException)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -488,6 +488,11 @@ namespace keepass2android.Io
|
|||||||
_cachedStorage.PrepareFileUsage(activity, ioc, requestCode, alwaysReturnSuccess || IsCached(ioc));
|
_cachedStorage.PrepareFileUsage(activity, ioc, requestCode, alwaysReturnSuccess || IsCached(ioc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
_cachedStorage.PrepareFileUsage(ctx, ioc);
|
||||||
|
}
|
||||||
|
|
||||||
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
_cachedStorage.OnCreate(activity, savedInstanceState);
|
_cachedStorage.OnCreate(activity, savedInstanceState);
|
||||||
@ -542,6 +547,19 @@ namespace keepass2android.Io
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
//even though the cache would be permanent, it's not a good idea to cache a temporary file, so return false in that case:
|
||||||
|
return _cachedStorage.IsPermanentLocation(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
//even though the cache can always be written, the changes made in the cache could not be transferred to the cached file
|
||||||
|
//so we better treat the cache as read-only as well.
|
||||||
|
return _cachedStorage.IsReadOnly(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
private void StoreFilePath(IOConnectionInfo folderPath, string filename, IOConnectionInfo res)
|
private void StoreFilePath(IOConnectionInfo folderPath, string filename, IOConnectionInfo res)
|
||||||
{
|
{
|
||||||
File.WriteAllText(CachedFilePath(GetPseudoIoc(folderPath, filename)) + ".filepath", res.Path);
|
File.WriteAllText(CachedFilePath(GetPseudoIoc(folderPath, filename)) + ".filepath", res.Path);
|
||||||
|
@ -157,6 +157,17 @@ namespace keepass2android.Io
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// The method may throw FileNotFoundException or not in case the file doesn't exist.
|
/// The method may throw FileNotFoundException or not in case the file doesn't exist.
|
||||||
IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename);
|
IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// returns true if it can be expected that this location will be available permanently (in contrast to a cache copy or temporary URI permissions in Android)
|
||||||
|
/// </summary>
|
||||||
|
/// Does not require to exist forever!
|
||||||
|
bool IsPermanentLocation(IOConnectionInfo ioc);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should return true if the file cannot be written.
|
||||||
|
/// </summary>
|
||||||
|
bool IsReadOnly(IOConnectionInfo ioc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IWriteTransaction: IDisposable
|
public interface IWriteTransaction: IDisposable
|
||||||
|
@ -283,6 +283,16 @@ namespace keepass2android.Io
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return false; //TODO implement. note, however, that we MAY return false even if it's read-only
|
||||||
|
}
|
||||||
|
|
||||||
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
_jfs.OnCreate(((IJavaFileStorageFileStorageSetupActivity)activity), savedInstanceState);
|
_jfs.OnCreate(((IJavaFileStorageFileStorageSetupActivity)activity), savedInstanceState);
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>8.0.30703</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{53A9CB7F-6553-4BC0-B56B-9410BB2E59AA}</ProjectGuid>
|
<ProjectGuid>{53A9CB7F-6553-4BC0-B56B-9410BB2E59AA}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -20,9 +18,10 @@
|
|||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
<DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;INCLUDE_KEYBOARD;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE;EXCLUDE_KEYTRANSFORM</DefineConstants>
|
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;INCLUDE_FILECHOOSER;_EXCLUDE_JAVAFILESTORAGE;INCLUDE_KEYTRANSFORM</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@ -31,6 +30,7 @@
|
|||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
||||||
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
||||||
@ -38,9 +38,10 @@
|
|||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
@ -64,6 +65,7 @@
|
|||||||
<Compile Include="DataExchange\Formats\KeePassKdb2x.cs" />
|
<Compile Include="DataExchange\Formats\KeePassKdb2x.cs" />
|
||||||
<Compile Include="DataExchange\Formats\KeePassXml2x.cs" />
|
<Compile Include="DataExchange\Formats\KeePassXml2x.cs" />
|
||||||
<Compile Include="DataExchange\PwExportInfo.cs" />
|
<Compile Include="DataExchange\PwExportInfo.cs" />
|
||||||
|
<Compile Include="Io\AndroidContentStorage.cs" />
|
||||||
<Compile Include="Io\BuiltInFileStorage.cs" />
|
<Compile Include="Io\BuiltInFileStorage.cs" />
|
||||||
<Compile Include="Io\CachingFileStorage.cs" />
|
<Compile Include="Io\CachingFileStorage.cs" />
|
||||||
<Compile Include="Io\DropboxFileStorage.cs" />
|
<Compile Include="Io\DropboxFileStorage.cs" />
|
||||||
@ -79,6 +81,7 @@
|
|||||||
<Compile Include="Io\SkyDriveFileStorage.cs" />
|
<Compile Include="Io\SkyDriveFileStorage.cs" />
|
||||||
<Compile Include="IProgressDialog.cs" />
|
<Compile Include="IProgressDialog.cs" />
|
||||||
<Compile Include="PreferenceKey.cs" />
|
<Compile Include="PreferenceKey.cs" />
|
||||||
|
<Compile Include="SelectStorageLocationActivityBase.cs" />
|
||||||
<Compile Include="UiStringKey.cs" />
|
<Compile Include="UiStringKey.cs" />
|
||||||
<Compile Include="database\Database.cs" />
|
<Compile Include="database\Database.cs" />
|
||||||
<Compile Include="database\edit\ActionOnFinish.cs" />
|
<Compile Include="database\edit\ActionOnFinish.cs" />
|
||||||
@ -113,19 +116,19 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
|
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
|
||||||
<Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
|
<Project>{48574278-4779-4B3A-A9E4-9CF1BC285D0B}</Project>
|
||||||
<Name>JavaFileStorageBindings</Name>
|
<Name>JavaFileStorageBindings</Name>
|
||||||
</ProjectReference>
|
</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>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
|
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
|
||||||
<Project>{70d3844a-d9fa-4a64-b205-a84c6a822196}</Project>
|
<Project>{70D3844A-D9FA-4A64-B205-A84C6A822196}</Project>
|
||||||
<Name>KP2AKdbLibraryBinding</Name>
|
<Name>KP2AKdbLibraryBinding</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj">
|
<ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj">
|
||||||
<Project>{5cf675a5-9bee-4720-bed9-d5bf14a2ebf9}</Project>
|
<Project>{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}</Project>
|
||||||
<Name>TwofishCipher</Name>
|
<Name>TwofishCipher</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
318
src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs
Normal file
318
src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
using System;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Widget;
|
||||||
|
using Java.Net;
|
||||||
|
using KeePassLib.Serialization;
|
||||||
|
using keepass2android.Io;
|
||||||
|
|
||||||
|
namespace keepass2android
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// base class for SelectStorageLocationActivity containing testable (non-UI) code
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SelectStorageLocationActivityBase: Activity
|
||||||
|
{
|
||||||
|
public enum WritableRequirements
|
||||||
|
{
|
||||||
|
ReadOnly = 0,
|
||||||
|
WriteDesired = 1,
|
||||||
|
WriteDemanded = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
protected const int RequestCodeFileStorageSelectionForPrimarySelect = 983713;
|
||||||
|
private const int RequestCodeFileStorageSelectionForCopyToWritableLocation = 983714;
|
||||||
|
private const int RequestCodeFileFileBrowseForWritableLocation = 983715;
|
||||||
|
private const int RequestCodeFileBrowseForOpen = 983716;
|
||||||
|
|
||||||
|
|
||||||
|
protected IOConnectionInfo _selectedIoc;
|
||||||
|
private IKp2aApp _app;
|
||||||
|
|
||||||
|
public SelectStorageLocationActivityBase(IKp2aApp app)
|
||||||
|
{
|
||||||
|
_app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||||
|
{
|
||||||
|
base.OnActivityResult(requestCode, resultCode, data);
|
||||||
|
if ((requestCode == RequestCodeFileStorageSelectionForPrimarySelect) || ((requestCode == RequestCodeFileStorageSelectionForCopyToWritableLocation)))
|
||||||
|
{
|
||||||
|
int browseRequestCode = RequestCodeFileBrowseForOpen;
|
||||||
|
if (requestCode == RequestCodeFileStorageSelectionForCopyToWritableLocation)
|
||||||
|
{
|
||||||
|
browseRequestCode = RequestCodeFileFileBrowseForWritableLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode == ExitFileStorageSelectionOk)
|
||||||
|
{
|
||||||
|
|
||||||
|
string protocolId = data.GetStringExtra("protocolId");
|
||||||
|
|
||||||
|
if (protocolId == "androidget")
|
||||||
|
{
|
||||||
|
ShowAndroidBrowseDialog(RequestCodeFileBrowseForOpen, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool isForSave = (requestCode == RequestCodeFileStorageSelectionForPrimarySelect) ?
|
||||||
|
IsStorageSelectionForSave : true;
|
||||||
|
|
||||||
|
|
||||||
|
StartSelectFile(isForSave, browseRequestCode, protocolId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReturnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((requestCode == RequestCodeFileBrowseForOpen) || (requestCode == RequestCodeFileFileBrowseForWritableLocation))
|
||||||
|
{
|
||||||
|
if (resultCode == (Result)FileStorageResults.FileChooserPrepared)
|
||||||
|
{
|
||||||
|
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||||
|
SetIoConnectionFromIntent(ioc, data);
|
||||||
|
bool isForSave = (requestCode == RequestCodeFileFileBrowseForWritableLocation) ?
|
||||||
|
true : IsStorageSelectionForSave;
|
||||||
|
|
||||||
|
StartFileChooser(ioc.Path, requestCode, isForSave);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((resultCode == Result.Canceled) && (data != null) && (data.HasExtra("EXTRA_ERROR_MESSAGE")))
|
||||||
|
{
|
||||||
|
ShowToast(data.GetStringExtra("EXTRA_ERROR_MESSAGE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode == Result.Ok)
|
||||||
|
{
|
||||||
|
string filename = IntentToFilename(data);
|
||||||
|
if (filename != null)
|
||||||
|
{
|
||||||
|
if (filename.StartsWith("file://"))
|
||||||
|
{
|
||||||
|
filename = filename.Substring(7);
|
||||||
|
filename = URLDecoder.Decode(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
IOConnectionInfo ioc = new IOConnectionInfo
|
||||||
|
{
|
||||||
|
Path = filename
|
||||||
|
};
|
||||||
|
|
||||||
|
IocSelected(ioc, requestCode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (data.Data.Scheme == "content")
|
||||||
|
{
|
||||||
|
IocSelected(IOConnectionInfo.FromPath(data.DataString), requestCode);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShowInvalidSchemeMessage(data.DataString);
|
||||||
|
ReturnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReturnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void ShowToast(string text);
|
||||||
|
|
||||||
|
protected abstract void ShowInvalidSchemeMessage(string dataString);
|
||||||
|
|
||||||
|
protected abstract string IntentToFilename(Intent data);
|
||||||
|
|
||||||
|
protected abstract void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent data);
|
||||||
|
|
||||||
|
protected abstract Result ExitFileStorageSelectionOk { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts the appropriate file selection process (either manual file select or prepare filechooser + filechooser)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isForSave"></param>
|
||||||
|
/// <param name="browseRequestCode"></param>
|
||||||
|
/// <param name="protocolId"></param>
|
||||||
|
protected abstract void StartSelectFile(bool isForSave, int browseRequestCode, string protocolId);
|
||||||
|
|
||||||
|
protected abstract void ShowAndroidBrowseDialog(int requestCode, bool isForSave);
|
||||||
|
|
||||||
|
protected abstract bool IsStorageSelectionForSave { get; }
|
||||||
|
|
||||||
|
|
||||||
|
private void IocSelected(IOConnectionInfo ioc, int requestCode)
|
||||||
|
{
|
||||||
|
if (requestCode == RequestCodeFileFileBrowseForWritableLocation)
|
||||||
|
{
|
||||||
|
IocForCopySelected(ioc);
|
||||||
|
}
|
||||||
|
else if (requestCode == RequestCodeFileBrowseForOpen)
|
||||||
|
{
|
||||||
|
PrimaryIocSelected(ioc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
throw new Exception("invalid request code!");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IocForCopySelected(IOConnectionInfo targetIoc)
|
||||||
|
{
|
||||||
|
PerformCopy(() =>
|
||||||
|
{
|
||||||
|
IOConnectionInfo sourceIoc = _selectedIoc;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CopyFile(targetIoc, sourceIoc);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
ShowToast(_app.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message);
|
||||||
|
ReturnCancel();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return () => { ReturnOk(targetIoc); };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void PerformCopy(Func<Action> copyAndReturnPostExecute);
|
||||||
|
|
||||||
|
private void MoveToWritableLocation(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
_selectedIoc = ioc;
|
||||||
|
|
||||||
|
StartFileStorageSelection(RequestCodeFileStorageSelectionForCopyToWritableLocation, false, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void StartFileStorageSelection(int requestCode,
|
||||||
|
bool allowThirdPartyGet, bool allowThirdPartySend);
|
||||||
|
|
||||||
|
protected bool OnReceivedSftpData(string filename, int requestCode, bool isForSave)
|
||||||
|
{
|
||||||
|
IOConnectionInfo ioc = new IOConnectionInfo { Path = filename };
|
||||||
|
#if !EXCLUDE_FILECHOOSER
|
||||||
|
StartFileChooser(ioc.Path, requestCode, isForSave);
|
||||||
|
#else
|
||||||
|
IocSelected(ioc, requestCode);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void StartFileChooser(string path, int requestCode, bool isForSave);
|
||||||
|
|
||||||
|
protected bool OnOpenButton(String fileName, int requestCode)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
IOConnectionInfo ioc = new IOConnectionInfo
|
||||||
|
{
|
||||||
|
Path = fileName
|
||||||
|
};
|
||||||
|
|
||||||
|
IocSelected(ioc, requestCode);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected virtual void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc)
|
||||||
|
{
|
||||||
|
IFileStorage sourceStorage = _app.GetFileStorage(sourceIoc, false); //don't cache source. file won't be used ever again
|
||||||
|
IFileStorage targetStorage = _app.GetFileStorage(targetIoc);
|
||||||
|
|
||||||
|
using (
|
||||||
|
var writeTransaction = targetStorage.OpenWriteTransaction(targetIoc,
|
||||||
|
_app.GetBooleanPreference(
|
||||||
|
PreferenceKey.UseFileTransactions)))
|
||||||
|
{
|
||||||
|
using (var writeStream = writeTransaction.OpenFile())
|
||||||
|
{
|
||||||
|
sourceStorage.OpenFileForRead(sourceIoc).CopyTo(writeStream);
|
||||||
|
}
|
||||||
|
writeTransaction.CommitWrite();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrimaryIocSelected(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
var filestorage = _app.GetFileStorage(ioc, false);
|
||||||
|
if (!filestorage.IsPermanentLocation(ioc))
|
||||||
|
{
|
||||||
|
|
||||||
|
string message = _app.GetResourceString(UiStringKey.FileIsTemporarilyAvailable) + " " + _app.GetResourceString(UiStringKey.CopyFileRequired) + " " + _app.GetResourceString(UiStringKey.ClickOkToSelectLocation);
|
||||||
|
EventHandler<DialogClickEventArgs> onOk = (sender, args) => { MoveToWritableLocation(ioc); };
|
||||||
|
EventHandler<DialogClickEventArgs> onCancel = (sender, args) => { ReturnCancel(); };
|
||||||
|
ShowAlertDialog(message, onOk, onCancel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ((RequestedWritableRequirements != WritableRequirements.ReadOnly) && (filestorage.IsReadOnly(ioc)))
|
||||||
|
{
|
||||||
|
string readOnlyExplanation = _app.GetResourceString(UiStringKey.FileIsReadOnly);
|
||||||
|
BuiltInFileStorage builtInFileStorage = filestorage as BuiltInFileStorage;
|
||||||
|
if (builtInFileStorage != null)
|
||||||
|
{
|
||||||
|
if (builtInFileStorage.IsReadOnlyBecauseKitkatRestrictions(ioc))
|
||||||
|
readOnlyExplanation = _app.GetResourceString(UiStringKey.FileIsReadOnlyOnKitkat);
|
||||||
|
}
|
||||||
|
EventHandler<DialogClickEventArgs> onOk = (sender, args) => { MoveToWritableLocation(ioc); };
|
||||||
|
EventHandler<DialogClickEventArgs> onCancel = (sender, args) =>
|
||||||
|
{
|
||||||
|
if (RequestedWritableRequirements == WritableRequirements.WriteDemanded)
|
||||||
|
ReturnCancel();
|
||||||
|
else
|
||||||
|
ReturnOk(ioc);
|
||||||
|
};
|
||||||
|
ShowAlertDialog(readOnlyExplanation + " "
|
||||||
|
+ (RequestedWritableRequirements == WritableRequirements.WriteDemanded ?
|
||||||
|
_app.GetResourceString(UiStringKey.CopyFileRequired)
|
||||||
|
: _app.GetResourceString(UiStringKey.CopyFileRequiredForEditing))
|
||||||
|
+ " "
|
||||||
|
+ _app.GetResourceString(UiStringKey.ClickOkToSelectLocation), onOk, onCancel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ReturnOk(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void ShowAlertDialog(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel);
|
||||||
|
|
||||||
|
protected abstract WritableRequirements RequestedWritableRequirements { get; }
|
||||||
|
|
||||||
|
protected abstract void ReturnOk(IOConnectionInfo ioc);
|
||||||
|
|
||||||
|
protected abstract void ReturnCancel();
|
||||||
|
}
|
||||||
|
}
|
@ -48,6 +48,12 @@ namespace keepass2android
|
|||||||
SynchronizingOtpAuxFile,
|
SynchronizingOtpAuxFile,
|
||||||
SavingOtpAuxFile,
|
SavingOtpAuxFile,
|
||||||
CertificateFailure,
|
CertificateFailure,
|
||||||
exporting_database
|
exporting_database,
|
||||||
|
FileIsTemporarilyAvailable,
|
||||||
|
CopyFileRequired,
|
||||||
|
ClickOkToSelectLocation,
|
||||||
|
FileIsReadOnly,
|
||||||
|
FileIsReadOnlyOnKitkat,
|
||||||
|
CopyFileRequiredForEditing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ namespace keepass2android
|
|||||||
KpDatabase = pwDatabase;
|
KpDatabase = pwDatabase;
|
||||||
SearchHelper = new SearchDbHelper(app);
|
SearchHelper = new SearchDbHelper(app);
|
||||||
|
|
||||||
CanWrite = databaseLoader.CanWrite;
|
CanWrite = databaseLoader.CanWrite && !fileStorage.IsReadOnly(iocInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -36,25 +36,21 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
KcpKeyFile passwordKeyfile = (KcpKeyFile)key.GetUserKey(typeof(KcpKeyFile));
|
KcpKeyFile passwordKeyfile = (KcpKeyFile)key.GetUserKey(typeof(KcpKeyFile));
|
||||||
string keyfile = "";
|
MemoryStream keyfileStream = null;
|
||||||
if (passwordKeyfile != null)
|
if (passwordKeyfile != null)
|
||||||
{
|
{
|
||||||
keyfile = passwordKeyfile.Path;
|
keyfileStream = new MemoryStream(passwordKeyfile.RawFileData.ReadData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dbv3 = importer.OpenDatabase(hashingStream, password, keyfile);
|
var dbv3 = importer.OpenDatabase(hashingStream, password, keyfileStream);
|
||||||
|
|
||||||
db.Name = dbv3.Name;
|
db.Name = dbv3.Name;
|
||||||
db.RootGroup = ConvertGroup(dbv3.RootGroup);
|
db.RootGroup = ConvertGroup(dbv3.RootGroup);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (InvalidPasswordException e) {
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (Java.IO.FileNotFoundException e)
|
catch (Java.IO.FileNotFoundException e)
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException(
|
throw new FileNotFoundException(
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>10.0.0</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}</ProjectGuid>
|
<ProjectGuid>{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -32,7 +30,6 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||||
<ConsolePause>False</ConsolePause>
|
<ConsolePause>False</ConsolePause>
|
||||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
||||||
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
||||||
@ -40,6 +37,8 @@
|
|||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>8.0.30703</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{46B769B8-2C58-4138-9CC0-70E3AE3C9A3A}</ProjectGuid>
|
<ProjectGuid>{46B769B8-2C58-4138-9CC0-70E3AE3C9A3A}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -16,7 +14,6 @@
|
|||||||
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
|
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
|
||||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||||
<AndroidUseLatestPlatformSdk />
|
|
||||||
<TargetFrameworkVersion>v4.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.2</TargetFrameworkVersion>
|
||||||
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
|
||||||
<AndroidStoreUncompressedFileExtensions />
|
<AndroidStoreUncompressedFileExtensions />
|
||||||
@ -32,7 +29,6 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
|
|
||||||
<AndroidLinkMode>None</AndroidLinkMode>
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
@ -43,10 +39,8 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
|
||||||
<AndroidLinkSkip>System.Core%3b</AndroidLinkSkip>
|
<AndroidLinkSkip>System.Core%3b</AndroidLinkSkip>
|
||||||
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
|
||||||
<BundleAssemblies>False</BundleAssemblies>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
||||||
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
||||||
@ -54,9 +48,10 @@
|
|||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
@ -67,6 +62,7 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="TestAndroidContentFileStorage.cs" />
|
||||||
<Compile Include="TestIntentsAndBundles.cs" />
|
<Compile Include="TestIntentsAndBundles.cs" />
|
||||||
<Compile Include="ProgressDialogStub.cs" />
|
<Compile Include="ProgressDialogStub.cs" />
|
||||||
<Compile Include="TestBase.cs" />
|
<Compile Include="TestBase.cs" />
|
||||||
@ -83,10 +79,10 @@
|
|||||||
<Compile Include="TestCachingFileStorage.cs" />
|
<Compile Include="TestCachingFileStorage.cs" />
|
||||||
<Compile Include="TestSaveDb.cs" />
|
<Compile Include="TestSaveDb.cs" />
|
||||||
<Compile Include="TestSaveDbCached.cs" />
|
<Compile Include="TestSaveDbCached.cs" />
|
||||||
|
<Compile Include="TestSelectStorageLocation.cs" />
|
||||||
<Compile Include="TestSynchronizeCachedDatabase.cs" />
|
<Compile Include="TestSynchronizeCachedDatabase.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="ClassDiagram1.cd" />
|
|
||||||
<None Include="Resources\AboutResources.txt" />
|
<None Include="Resources\AboutResources.txt" />
|
||||||
<None Include="Assets\AboutAssets.txt" />
|
<None Include="Assets\AboutAssets.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -109,7 +105,7 @@
|
|||||||
<Name>Kp2aBusinessLogic</Name>
|
<Name>Kp2aBusinessLogic</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
|
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
|
||||||
<Project>{70d3844a-d9fa-4a64-b205-a84c6a822196}</Project>
|
<Project>{70D3844A-D9FA-4A64-B205-A84C6A822196}</Project>
|
||||||
<Name>KP2AKdbLibraryBinding</Name>
|
<Name>KP2AKdbLibraryBinding</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\monodroid-unittesting\MonoDroidUnitTesting\MonoDroidUnitTesting.csproj">
|
<ProjectReference Include="..\monodroid-unittesting\MonoDroidUnitTesting\MonoDroidUnitTesting.csproj">
|
||||||
|
@ -19,7 +19,11 @@ namespace Kp2aUnitTests
|
|||||||
TestRunner runner = new TestRunner();
|
TestRunner runner = new TestRunner();
|
||||||
// Run all tests from this assembly
|
// Run all tests from this assembly
|
||||||
//runner.AddTests(Assembly.GetExecutingAssembly());
|
//runner.AddTests(Assembly.GetExecutingAssembly());
|
||||||
runner.AddTests(new List<Type> { typeof(TestIntentsAndBundles) });
|
//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileByDirectCall"));
|
||||||
|
//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileOnly"));
|
||||||
|
|
||||||
|
|
||||||
|
runner.AddTests(new List<Type> { typeof(TestSelectStorageLocation) });
|
||||||
//runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase)});
|
//runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase)});
|
||||||
//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure"));
|
//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure"));
|
||||||
//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadWithAcceptedCertificateTrustFailure"));
|
//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadWithAcceptedCertificateTrustFailure"));
|
||||||
|
33
src/Kp2aUnitTests/TestAndroidContentFileStorage.cs
Normal file
33
src/Kp2aUnitTests/TestAndroidContentFileStorage.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
|
using Java.IO;
|
||||||
|
using KeePassLib;
|
||||||
|
using KeePassLib.Interfaces;
|
||||||
|
using KeePassLib.Keys;
|
||||||
|
using KeePassLib.Serialization;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using keepass2android;
|
||||||
|
using keepass2android.Io;
|
||||||
|
|
||||||
|
namespace Kp2aUnitTests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
internal class TestBuiltInFileStorage
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void ReadOnlyKitKat()
|
||||||
|
{
|
||||||
|
var storage = new BuiltInFileStorage(new TestKp2aApp());
|
||||||
|
var extFile = "/storage/sdcard1/file.txt";
|
||||||
|
Assert.IsTrue(storage.IsReadOnly(IOConnectionInfo.FromPath(extFile)));
|
||||||
|
Assert.IsTrue(storage.IsReadOnly(IOConnectionInfo.FromPath(extFile)));
|
||||||
|
|
||||||
|
Assert.IsFalse(storage.IsReadOnly(IOConnectionInfo.FromPath(Application.Context.GetExternalFilesDir(null).AbsolutePath+ "/file.txt")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -183,5 +183,15 @@ namespace Kp2aUnitTests
|
|||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -134,11 +134,18 @@ namespace Kp2aUnitTests
|
|||||||
return new ProgressDialogStub();
|
return new ProgressDialogStub();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
|
public virtual IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
|
||||||
{
|
{
|
||||||
return FileStorage;
|
return FileStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache)
|
||||||
|
{
|
||||||
|
if (FileStorage is CachingFileStorage)
|
||||||
|
throw new Exception("bad test class");
|
||||||
|
return FileStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool TriggerReloadCalled;
|
public bool TriggerReloadCalled;
|
||||||
private TestFileStorage _testFileStorage;
|
private TestFileStorage _testFileStorage;
|
||||||
|
@ -2,11 +2,15 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
|
using Com.Keepassdroid.Database.Load;
|
||||||
|
using Java.IO;
|
||||||
using KeePassLib;
|
using KeePassLib;
|
||||||
|
using KeePassLib.Keys;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using keepass2android;
|
using keepass2android;
|
||||||
using keepass2android.Io;
|
using keepass2android.Io;
|
||||||
|
using FileNotFoundException = System.IO.FileNotFoundException;
|
||||||
|
|
||||||
namespace Kp2aUnitTests
|
namespace Kp2aUnitTests
|
||||||
{
|
{
|
||||||
@ -72,6 +76,56 @@ namespace Kp2aUnitTests
|
|||||||
Assert.IsFalse(e.Binaries.Any());
|
Assert.IsFalse(e.Binaries.Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestLoadKdb1WithKeyfileByDirectCall()
|
||||||
|
{
|
||||||
|
ImporterV3 importer = new ImporterV3();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileStream dbStream = new FileStream(TestDbDirectory+"withkeyfile_nopwd.kdb", FileMode.Open);
|
||||||
|
FileStream keyfileStream = new FileStream(TestDbDirectory + "withkeyfile.key", FileMode.Open);
|
||||||
|
/*
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
int b = keyfileStream.ReadByte();
|
||||||
|
Kp2aLog.Log(i+": " + b);
|
||||||
|
}
|
||||||
|
keyfileStream.Close();
|
||||||
|
Kp2aLog.Log("stream 2");
|
||||||
|
var keyfileStream2 = new MemoryStream(new KcpKeyFile(TestDbDirectory + "withkeyfile.key").RawFileData.ReadData());
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
int b = keyfileStream2.ReadByte();
|
||||||
|
Kp2aLog.Log(i + ": " + b);
|
||||||
|
}*/
|
||||||
|
importer.OpenDatabase(dbStream, "", keyfileStream);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Kp2aLog.Log(e.ToString());
|
||||||
|
Assert.Fail("exception occured: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestLoadKdb1WithKeyfile()
|
||||||
|
{
|
||||||
|
var app = PerformLoad("withkeyfile.kdb", "test", TestDbDirectory + "withkeyfile.key");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestLoadKdb1WithKeyfileOnly()
|
||||||
|
{
|
||||||
|
var app = PerformLoad("withkeyfile_nopwd.kdb", "", TestDbDirectory + "withkeyfile.key");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestLoadWithKeyfileOnly()
|
public void TestLoadWithKeyfileOnly()
|
||||||
{
|
{
|
||||||
|
790
src/Kp2aUnitTests/TestSelectStorageLocation.cs
Normal file
790
src/Kp2aUnitTests/TestSelectStorageLocation.cs
Normal file
@ -0,0 +1,790 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
|
using Android.Runtime;
|
||||||
|
using Android.Views;
|
||||||
|
using Android.Widget;
|
||||||
|
using KeePassLib.Serialization;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using keepass2android;
|
||||||
|
using keepass2android.Io;
|
||||||
|
|
||||||
|
namespace Kp2aUnitTests
|
||||||
|
{
|
||||||
|
|
||||||
|
class TemporaryFileStorage: IFileStorage
|
||||||
|
{
|
||||||
|
public IEnumerable<string> SupportedProtocols
|
||||||
|
{
|
||||||
|
get {
|
||||||
|
yield return "content";
|
||||||
|
yield return "readonly";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream OpenFileForRead(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RequiresCredentials(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileDescription GetFileDescription(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
|
||||||
|
bool alwaysReturnSuccess)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnResume(IFileStorageSetupActivity activity)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnStart(IFileStorageSetupActivity activity)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetDisplayName(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateFilePath(string parent, string newFilename)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return ioc.Path.StartsWith("content") == false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestKp2aAppForSelectStorageLocation: TestKp2aApp
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public override IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache)
|
||||||
|
{
|
||||||
|
if ((iocInfo.Path.StartsWith("content://")) || (iocInfo.Path.StartsWith("readonly://")))
|
||||||
|
{
|
||||||
|
return new TemporaryFileStorage();
|
||||||
|
}
|
||||||
|
return base.GetFileStorage(iocInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class TestControllableSelectStorageLocationActivity: SelectStorageLocationActivityBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public List<string> toasts = new List<string>();
|
||||||
|
public WritableRequirements requestedWritableRequirements;
|
||||||
|
public bool? _result;
|
||||||
|
public IOConnectionInfo _resultIoc;
|
||||||
|
public object _userAction;
|
||||||
|
private IKp2aApp _app;
|
||||||
|
|
||||||
|
public TestControllableSelectStorageLocationActivity(IKp2aApp app) : base(app)
|
||||||
|
{
|
||||||
|
_app = app;
|
||||||
|
StartFileStorageSelection(RequestCodeFileStorageSelectionForPrimarySelect, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ShowToast(string text)
|
||||||
|
{
|
||||||
|
toasts.Add(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc)
|
||||||
|
{
|
||||||
|
if (CopyFileShouldFail)
|
||||||
|
{
|
||||||
|
throw new Exception("CopyFile failed in test.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CopyFileShouldFail { get; set; }
|
||||||
|
|
||||||
|
protected override void ShowInvalidSchemeMessage(string dataString)
|
||||||
|
{
|
||||||
|
toasts.Add("invalid scheme: " + dataString);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string IntentToFilename(Intent data)
|
||||||
|
{
|
||||||
|
return data.GetStringExtra("path");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent data)
|
||||||
|
{
|
||||||
|
ioc.Path = data.GetStringExtra("path");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result ExitFileStorageSelectionOk
|
||||||
|
{
|
||||||
|
get { return Result.FirstUser + 825; }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void StartSelectFile(bool isForSave, int browseRequestCode, string protocolId)
|
||||||
|
{
|
||||||
|
_userAction = new SelectFileAction(isForSave, browseRequestCode, protocolId, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleActivityResult(int requestCode, Result resultCode, Intent data)
|
||||||
|
{
|
||||||
|
OnActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class SelectFileAction
|
||||||
|
{
|
||||||
|
private readonly bool _isForSave;
|
||||||
|
private readonly int _browseRequestCode;
|
||||||
|
private readonly string _protocolId;
|
||||||
|
private readonly TestControllableSelectStorageLocationActivity _testControllableSelectStorageLocationActivity;
|
||||||
|
|
||||||
|
public SelectFileAction(bool isForSave, int browseRequestCode, string protocolId, TestControllableSelectStorageLocationActivity testControllableSelectStorageLocationActivity)
|
||||||
|
{
|
||||||
|
_isForSave = isForSave;
|
||||||
|
_browseRequestCode = browseRequestCode;
|
||||||
|
_protocolId = protocolId;
|
||||||
|
_testControllableSelectStorageLocationActivity = testControllableSelectStorageLocationActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsForSave {
|
||||||
|
get { return _isForSave; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int BrowseRequestCode
|
||||||
|
{
|
||||||
|
get { return _browseRequestCode; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ProtocolId
|
||||||
|
{
|
||||||
|
get { return _protocolId; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void PerformManualFileSelect(string path)
|
||||||
|
{
|
||||||
|
_testControllableSelectStorageLocationActivity.PressOpenButton(path, _browseRequestCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
_testControllableSelectStorageLocationActivity.ReturnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareFileChooser(string protocolId)
|
||||||
|
{
|
||||||
|
Intent data = new Intent();
|
||||||
|
data.PutExtra("path", protocolId+"://");
|
||||||
|
_testControllableSelectStorageLocationActivity.HandleActivityResult(_browseRequestCode, (Result) FileStorageResults.FileChooserPrepared, data);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PressOpenButton(string path, int browseRequestCode)
|
||||||
|
{
|
||||||
|
OnOpenButton(path, browseRequestCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class FileStorageSelectionAction
|
||||||
|
{
|
||||||
|
private readonly int _requestCode;
|
||||||
|
private readonly bool _allowThirdPartyGet;
|
||||||
|
private readonly bool _allowThirdPartySend;
|
||||||
|
private readonly TestControllableSelectStorageLocationActivity _testControllableSelectStorageLocationActivity;
|
||||||
|
|
||||||
|
public FileStorageSelectionAction(int requestCode, bool allowThirdPartyGet, bool allowThirdPartySend, TestControllableSelectStorageLocationActivity testControllableSelectStorageLocationActivity)
|
||||||
|
{
|
||||||
|
_requestCode = requestCode;
|
||||||
|
_allowThirdPartyGet = allowThirdPartyGet;
|
||||||
|
_allowThirdPartySend = allowThirdPartySend;
|
||||||
|
_testControllableSelectStorageLocationActivity = testControllableSelectStorageLocationActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RequestCode
|
||||||
|
{
|
||||||
|
get { return _requestCode; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AllowThirdPartyGet
|
||||||
|
{
|
||||||
|
get { return _allowThirdPartyGet; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AllowThirdPartySend
|
||||||
|
{
|
||||||
|
get { return _allowThirdPartySend; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReturnProtocol(string protocolId)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.PutExtra("protocolId", protocolId);
|
||||||
|
_testControllableSelectStorageLocationActivity.HandleActivityResult(_requestCode, Result.FirstUser + 825 /*fs select ok*/, intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
_testControllableSelectStorageLocationActivity.HandleActivityResult(_requestCode, Result.Canceled, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ShowAndroidBrowseDialog(int requestCode, bool isForSave)
|
||||||
|
{
|
||||||
|
_userAction = new AndroidBrowseDialogAction(requestCode, isForSave, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AndroidBrowseDialogAction
|
||||||
|
{
|
||||||
|
private readonly int _requestCode;
|
||||||
|
private readonly bool _isForSave;
|
||||||
|
private readonly TestControllableSelectStorageLocationActivity _activity;
|
||||||
|
|
||||||
|
public AndroidBrowseDialogAction(int requestCode, bool isForSave, TestControllableSelectStorageLocationActivity activity)
|
||||||
|
{
|
||||||
|
_requestCode = requestCode;
|
||||||
|
_isForSave = isForSave;
|
||||||
|
_activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RequestCode
|
||||||
|
{
|
||||||
|
get { return _requestCode; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReturnSelectedFile(string selectedUri)
|
||||||
|
{
|
||||||
|
Intent data = new Intent();
|
||||||
|
data.PutExtra("path", selectedUri);
|
||||||
|
_activity.HandleActivityResult(_requestCode, Result.Ok, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
_activity.HandleActivityResult(_requestCode, Result.Canceled, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool IsStorageSelectionForSave { get { return SelectLocationForSave; } }
|
||||||
|
|
||||||
|
private bool SelectLocationForSave { get; set; }
|
||||||
|
|
||||||
|
protected override void PerformCopy(Func<Action> copyAndReturnPostExecute)
|
||||||
|
{
|
||||||
|
Action postExec = copyAndReturnPostExecute();
|
||||||
|
postExec();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void StartFileStorageSelection(int requestCode, bool allowThirdPartyGet, bool allowThirdPartySend)
|
||||||
|
{
|
||||||
|
_userAction = new FileStorageSelectionAction(requestCode, allowThirdPartyGet, allowThirdPartySend, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void StartFileChooser(string path, int requestCode, bool isForSave)
|
||||||
|
{
|
||||||
|
_userAction = new FileChooserAction(path, requestCode, isForSave, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class FileChooserAction
|
||||||
|
{
|
||||||
|
private readonly string _path;
|
||||||
|
private readonly int _requestCode;
|
||||||
|
private readonly bool _isForSave;
|
||||||
|
private readonly TestControllableSelectStorageLocationActivity _activity;
|
||||||
|
|
||||||
|
public FileChooserAction(string path, int requestCode, bool isForSave, TestControllableSelectStorageLocationActivity activity)
|
||||||
|
{
|
||||||
|
_path = path;
|
||||||
|
_requestCode = requestCode;
|
||||||
|
_isForSave = isForSave;
|
||||||
|
_activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Path
|
||||||
|
{
|
||||||
|
get { return _path; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RequestCode
|
||||||
|
{
|
||||||
|
get { return _requestCode; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsForSave
|
||||||
|
{
|
||||||
|
get { return _isForSave; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReturnChosenFile(string path)
|
||||||
|
{
|
||||||
|
Intent data = new Intent();
|
||||||
|
data.PutExtra("path", path);
|
||||||
|
_activity.HandleActivityResult(_requestCode, Result.Ok, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
_activity.HandleActivityResult(_requestCode, Result.Canceled, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ShowAlertDialog(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel)
|
||||||
|
{
|
||||||
|
_userAction = new ShowAlertDialogAction(message, onOk, onCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ShowAlertDialogAction
|
||||||
|
{
|
||||||
|
public string Message { get; set; }
|
||||||
|
public EventHandler<DialogClickEventArgs> OnOk { get; set; }
|
||||||
|
public EventHandler<DialogClickEventArgs> OnCancel { get; set; }
|
||||||
|
|
||||||
|
public ShowAlertDialogAction(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
OnOk = onOk;
|
||||||
|
OnCancel = onCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
OnCancel(this, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Ok()
|
||||||
|
{
|
||||||
|
OnOk(this, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override WritableRequirements RequestedWritableRequirements
|
||||||
|
{
|
||||||
|
get { return requestedWritableRequirements; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IKp2aApp App
|
||||||
|
{
|
||||||
|
get { return _app; }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ReturnOk(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
_result = true;
|
||||||
|
_resultIoc = ioc;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ReturnCancel()
|
||||||
|
{
|
||||||
|
_result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
class TestSelectStorageLocation
|
||||||
|
{
|
||||||
|
[TestInitialize]
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Looper.Prepare();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCancelFileStorageSelection()
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action.Cancel();
|
||||||
|
Assert.IsFalse((bool) testee._result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestSimpleManualSelect()
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction) testee._userAction;
|
||||||
|
action.ReturnProtocol("ftp");
|
||||||
|
|
||||||
|
Assert.IsNull(testee._result); //no result yet
|
||||||
|
|
||||||
|
var action2 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
|
||||||
|
string path = "ftp://crocoll.net/test.kdbx";
|
||||||
|
action2.PerformManualFileSelect(path);
|
||||||
|
|
||||||
|
Assert.IsTrue((bool) testee._result);
|
||||||
|
Assert.AreEqual(testee._resultIoc.Path, path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCancelManualSelect()
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action.ReturnProtocol("ftp");
|
||||||
|
|
||||||
|
Assert.IsNull(testee._result); //no result yet
|
||||||
|
|
||||||
|
var action2 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
|
||||||
|
action2.Cancel();
|
||||||
|
|
||||||
|
Assert.IsFalse((bool)testee._result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCancelAndroidBrowseDialog()
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action.ReturnProtocol("androidget");
|
||||||
|
|
||||||
|
Assert.IsNull(testee._result); //no result yet
|
||||||
|
|
||||||
|
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
|
||||||
|
action2.Cancel();
|
||||||
|
|
||||||
|
Assert.IsFalse((bool)testee._result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCancelCopyTemporaryLocation()
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action.ReturnProtocol("androidget");
|
||||||
|
|
||||||
|
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
|
||||||
|
action2.ReturnSelectedFile("content://abc.kdbx");
|
||||||
|
|
||||||
|
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable)));
|
||||||
|
Assert.IsNull(testee._result); //no result yet
|
||||||
|
action3.Cancel();
|
||||||
|
|
||||||
|
Assert.IsFalse((bool)testee._result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCopyTemporaryLocation()
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action.ReturnProtocol("androidget");
|
||||||
|
|
||||||
|
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
|
||||||
|
action2.ReturnSelectedFile("content://abc.kdbx");
|
||||||
|
|
||||||
|
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable)));
|
||||||
|
Assert.IsNull(testee._result); //no result yet
|
||||||
|
action3.Ok();
|
||||||
|
|
||||||
|
|
||||||
|
var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action4.ReturnProtocol("ftp");
|
||||||
|
|
||||||
|
Assert.IsNull(testee._result);
|
||||||
|
|
||||||
|
var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action5.IsForSave);
|
||||||
|
string path = "ftp://crocoll.net/testtarget.kdbx";
|
||||||
|
action5.PerformManualFileSelect(path);
|
||||||
|
|
||||||
|
Assert.IsTrue((bool)testee._result);
|
||||||
|
Assert.AreEqual(path, testee._resultIoc.Path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCopyTemporaryLocationWithFileBrowser()
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action.ReturnProtocol("androidget");
|
||||||
|
|
||||||
|
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
|
||||||
|
action2.ReturnSelectedFile("content://abc.kdbx");
|
||||||
|
|
||||||
|
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable)));
|
||||||
|
Assert.IsNull(testee._result); //no result yet
|
||||||
|
action3.Ok();
|
||||||
|
|
||||||
|
|
||||||
|
var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action4.ReturnProtocol("file");
|
||||||
|
|
||||||
|
|
||||||
|
var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action5.IsForSave);
|
||||||
|
|
||||||
|
action5.PrepareFileChooser("file");
|
||||||
|
|
||||||
|
Assert.IsNull(testee._result);
|
||||||
|
|
||||||
|
|
||||||
|
var action6 = (TestControllableSelectStorageLocationActivity.FileChooserAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action5.IsForSave);
|
||||||
|
string path = "file:///mnt/sdcard/testtarget.kdbx";
|
||||||
|
|
||||||
|
action6.ReturnChosenFile(path);
|
||||||
|
|
||||||
|
string expectedpath = "/mnt/sdcard/testtarget.kdbx";
|
||||||
|
Assert.IsTrue((bool)testee._result);
|
||||||
|
Assert.AreEqual(expectedpath, testee._resultIoc.Path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCopyTemporaryLocationWithCancelFileBrowser()
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action.ReturnProtocol("androidget");
|
||||||
|
|
||||||
|
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
|
||||||
|
action2.ReturnSelectedFile("content://abc.kdbx");
|
||||||
|
|
||||||
|
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable)));
|
||||||
|
Assert.IsNull(testee._result); //no result yet
|
||||||
|
action3.Ok();
|
||||||
|
|
||||||
|
|
||||||
|
var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action4.ReturnProtocol("file");
|
||||||
|
|
||||||
|
|
||||||
|
var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action5.IsForSave);
|
||||||
|
|
||||||
|
action5.PrepareFileChooser("file");
|
||||||
|
|
||||||
|
Assert.IsNull(testee._result);
|
||||||
|
|
||||||
|
|
||||||
|
var action6 = (TestControllableSelectStorageLocationActivity.FileChooserAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action5.IsForSave);
|
||||||
|
string path = "file:///mnt/sdcard/testtarget.kdbx";
|
||||||
|
|
||||||
|
action6.Cancel();
|
||||||
|
|
||||||
|
Assert.IsFalse((bool)testee._result);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCancelCopyReadOnlyLocation()
|
||||||
|
{
|
||||||
|
SelectStorageLocationActivityBase.WritableRequirements requestedWritableRequirements = SelectStorageLocationActivityBase.WritableRequirements.WriteDesired;
|
||||||
|
string path;
|
||||||
|
var testee = PrepareTesteeForCancelCopyReadOnly(requestedWritableRequirements, out path);
|
||||||
|
|
||||||
|
Assert.IsTrue((bool)testee._result);
|
||||||
|
Assert.AreEqual(path, testee._resultIoc.Path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCancelCopyReadOnlyLocationWriteRequired()
|
||||||
|
{
|
||||||
|
SelectStorageLocationActivityBase.WritableRequirements requestedWritableRequirements = SelectStorageLocationActivityBase.WritableRequirements.WriteDemanded;
|
||||||
|
string path;
|
||||||
|
var testee = PrepareTesteeForCancelCopyReadOnly(requestedWritableRequirements, out path);
|
||||||
|
|
||||||
|
Assert.IsFalse((bool)testee._result);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TestControllableSelectStorageLocationActivity PrepareTesteeForCancelCopyReadOnly(
|
||||||
|
SelectStorageLocationActivityBase.WritableRequirements requestedWritableRequirements, out string path)
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
|
||||||
|
testee.requestedWritableRequirements = requestedWritableRequirements;
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction) testee._userAction;
|
||||||
|
action.ReturnProtocol("androidget");
|
||||||
|
|
||||||
|
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction) testee._userAction;
|
||||||
|
path = "readonly://abc.kdbx";
|
||||||
|
action2.ReturnSelectedFile(path);
|
||||||
|
|
||||||
|
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction) testee._userAction;
|
||||||
|
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsReadOnly)));
|
||||||
|
Assert.IsNull(testee._result); //no result yet
|
||||||
|
action3.Cancel();
|
||||||
|
return testee;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestOpenReadOnly()
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
|
||||||
|
testee.requestedWritableRequirements = SelectStorageLocationActivityBase.WritableRequirements.ReadOnly;
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction) testee._userAction;
|
||||||
|
action.ReturnProtocol("androidget");
|
||||||
|
|
||||||
|
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction) testee._userAction;
|
||||||
|
var path = "readonly://abc.kdbx";
|
||||||
|
action2.ReturnSelectedFile(path);
|
||||||
|
|
||||||
|
Assert.IsTrue((bool)testee._result);
|
||||||
|
Assert.AreEqual(path, testee._resultIoc.Path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCopyTemporaryLocationFails()
|
||||||
|
{
|
||||||
|
var testee = CreateTestee();
|
||||||
|
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action.ReturnProtocol("androidget");
|
||||||
|
|
||||||
|
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
|
||||||
|
action2.ReturnSelectedFile("content://abc.kdbx");
|
||||||
|
|
||||||
|
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable)));
|
||||||
|
Assert.IsNull(testee._result); //no result yet
|
||||||
|
action3.Ok();
|
||||||
|
|
||||||
|
|
||||||
|
var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
|
||||||
|
action4.ReturnProtocol("ftp");
|
||||||
|
|
||||||
|
Assert.IsNull(testee._result);
|
||||||
|
|
||||||
|
var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
|
||||||
|
Assert.IsTrue(action5.IsForSave);
|
||||||
|
string path = "ftp://crocoll.net/testtarget.kdbx";
|
||||||
|
|
||||||
|
testee.CopyFileShouldFail = true;
|
||||||
|
|
||||||
|
action5.PerformManualFileSelect(path);
|
||||||
|
|
||||||
|
Assert.IsFalse((bool)testee._result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static TestControllableSelectStorageLocationActivity CreateTestee()
|
||||||
|
{
|
||||||
|
return new TestControllableSelectStorageLocationActivity(new TestKp2aAppForSelectStorageLocation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,10 +35,6 @@
|
|||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
<HintPath>..\..\..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
@ -46,6 +42,9 @@
|
|||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework">
|
||||||
|
<HintPath>..\..\..\..\Program Files %28x86%29\Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Main.cs" />
|
<Compile Include="Main.cs" />
|
||||||
@ -57,7 +56,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MasterPassword\MasterPassword.csproj">
|
<ProjectReference Include="..\MasterPassword\MasterPassword.csproj">
|
||||||
<Project>{2f7cb5b4-ac2a-4790-b0f3-42e6c9a060d5}</Project>
|
<Project>{2F7CB5B4-AC2A-4790-B0F3-42E6C9A060D5}</Project>
|
||||||
<Name>MasterPassword</Name>
|
<Name>MasterPassword</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>8.0.30703</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{9A4C5BAA-1A8A-49B4-BBC3-60D4871FB36C}</ProjectGuid>
|
<ProjectGuid>{9A4C5BAA-1A8A-49B4-BBC3-60D4871FB36C}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -26,7 +24,6 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
|
|
||||||
<AndroidLinkMode>None</AndroidLinkMode>
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
@ -37,7 +34,6 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
@ -86,11 +82,11 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MasterPassword\MasterPassword.csproj">
|
<ProjectReference Include="..\MasterPassword\MasterPassword.csproj">
|
||||||
<Project>{2f7cb5b4-ac2a-4790-b0f3-42e6c9a060d5}</Project>
|
<Project>{2F7CB5B4-AC2A-4790-B0F3-42E6C9A060D5}</Project>
|
||||||
<Name>MasterPassword</Name>
|
<Name>MasterPassword</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj">
|
<ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj">
|
||||||
<Project>{3da3911e-36de-465e-8f15-f1991b6437e5}</Project>
|
<Project>{3DA3911E-36DE-465E-8F15-F1991B6437E5}</Project>
|
||||||
<Name>PluginSdkBinding</Name>
|
<Name>PluginSdkBinding</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>8.0.30703</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}</ProjectGuid>
|
<ProjectGuid>{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -16,7 +14,6 @@
|
|||||||
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
|
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
|
||||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||||
<AndroidUseLatestPlatformSdk />
|
|
||||||
<TargetFrameworkVersion>v4.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.2</TargetFrameworkVersion>
|
||||||
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
|
||||||
<AndroidStoreUncompressedFileExtensions />
|
<AndroidStoreUncompressedFileExtensions />
|
||||||
@ -32,7 +29,6 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
|
|
||||||
<AndroidLinkMode>None</AndroidLinkMode>
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
@ -43,7 +39,6 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
@ -122,7 +117,7 @@
|
|||||||
<Name>KeePassLib2Android</Name>
|
<Name>KeePassLib2Android</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj">
|
<ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj">
|
||||||
<Project>{3da3911e-36de-465e-8f15-f1991b6437e5}</Project>
|
<Project>{3DA3911E-36DE-465E-8F15-F1991B6437E5}</Project>
|
||||||
<Name>PluginSdkBinding</Name>
|
<Name>PluginSdkBinding</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>8.0.30703</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{3DA3911E-36DE-465E-8F15-F1991B6437E5}</ProjectGuid>
|
<ProjectGuid>{3DA3911E-36DE-465E-8F15-F1991B6437E5}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -22,6 +20,7 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@ -30,6 +29,7 @@
|
|||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
||||||
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
||||||
@ -39,6 +39,8 @@
|
|||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>8.0.30703</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}</ProjectGuid>
|
<ProjectGuid>{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -23,6 +21,7 @@
|
|||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@ -31,6 +30,7 @@
|
|||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
||||||
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
||||||
@ -38,9 +38,10 @@
|
|||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
@ -66,7 +67,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<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>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.inputstick.api"
|
package="com.inputstick.api"
|
||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
|
@ -14,7 +14,10 @@ public final class R {
|
|||||||
public static int ic_launcher=0x7f020000;
|
public static int ic_launcher=0x7f020000;
|
||||||
}
|
}
|
||||||
public static final class string {
|
public static final class string {
|
||||||
|
public static int action_settings=0x7f030002;
|
||||||
public static int app_name=0x7f030000;
|
public static int app_name=0x7f030000;
|
||||||
|
public static int hello_world=0x7f030003;
|
||||||
|
public static int title_activity_install_utility=0x7f030001;
|
||||||
}
|
}
|
||||||
public static final class style {
|
public static final class style {
|
||||||
/**
|
/**
|
||||||
|
Binary file not shown.
@ -1,5 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<string name="app_name">InputStickAPI</string>
|
<string name="app_name">InputStickAPI</string>
|
||||||
|
<string name="title_activity_install_utility">Download InputStickUtility</string>
|
||||||
|
<string name="action_settings">Settings</string>
|
||||||
|
<string name="hello_world">Hello world!</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -10,8 +10,10 @@ public class AES {
|
|||||||
private Cipher mCipherEncr;
|
private Cipher mCipherEncr;
|
||||||
private Cipher mCipherDecr;
|
private Cipher mCipherDecr;
|
||||||
private SecretKeySpec mKey;
|
private SecretKeySpec mKey;
|
||||||
|
private boolean ready;
|
||||||
|
|
||||||
public AES() {
|
public AES() {
|
||||||
|
ready = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] getMD5(String s) {
|
public static byte[] getMD5(String s) {
|
||||||
@ -31,10 +33,10 @@ public class AES {
|
|||||||
mCipherEncr = Cipher.getInstance("AES/CBC/NoPadding");
|
mCipherEncr = Cipher.getInstance("AES/CBC/NoPadding");
|
||||||
mCipherEncr.init(Cipher.ENCRYPT_MODE, mKey);
|
mCipherEncr.init(Cipher.ENCRYPT_MODE, mKey);
|
||||||
iv = mCipherEncr.getIV();
|
iv = mCipherEncr.getIV();
|
||||||
//System.out.println("AES IV: ");
|
Util.printHex(iv, "AES IV: ");
|
||||||
Util.printHex(iv);
|
|
||||||
mCipherDecr = Cipher.getInstance("AES/CBC/NoPadding");
|
mCipherDecr = Cipher.getInstance("AES/CBC/NoPadding");
|
||||||
mCipherDecr.init(Cipher.DECRYPT_MODE, mKey, new IvParameterSpec(iv));
|
mCipherDecr.init(Cipher.DECRYPT_MODE, mKey, new IvParameterSpec(iv));
|
||||||
|
ready = true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -49,4 +51,7 @@ public class AES {
|
|||||||
return mCipherDecr.update(data);
|
return mCipherDecr.update(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isReady() {
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
|||||||
private Application mApp;
|
private Application mApp;
|
||||||
private BTService mBTService;
|
private BTService mBTService;
|
||||||
private PacketManager mPacketManager;
|
private PacketManager mPacketManager;
|
||||||
//private PacketQueue mPacketQueue;
|
|
||||||
private final BTHandler mBTHandler = new BTHandler(this);
|
private final BTHandler mBTHandler = new BTHandler(this);
|
||||||
|
|
||||||
|
|
||||||
@ -45,31 +44,12 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
|||||||
break;
|
break;
|
||||||
case BTService.EVENT_CANCELLED:
|
case BTService.EVENT_CANCELLED:
|
||||||
manager.onDisconnected();
|
manager.onDisconnected();
|
||||||
break;
|
break;
|
||||||
case BTService.EVENT_CONNECTION_FAILED:
|
case BTService.EVENT_ERROR:
|
||||||
manager.onFailure(1);
|
manager.onFailure(msg.arg1);
|
||||||
break;
|
break;
|
||||||
case BTService.EVENT_CONNECTION_LOST:
|
default:
|
||||||
manager.onFailure(1);
|
manager.onFailure(InputStickError.ERROR_BLUETOOTH);
|
||||||
break;
|
|
||||||
case BTService.EVENT_NO_BT_HW:
|
|
||||||
manager.onFailure(1);
|
|
||||||
break;
|
|
||||||
case BTService.EVENT_INVALID_MAC:
|
|
||||||
manager.onFailure(1);
|
|
||||||
break;
|
|
||||||
case BTService.EVENT_CMD_TIMEOUT:
|
|
||||||
manager.onFailure(1);
|
|
||||||
break;
|
|
||||||
case BTService.EVENT_INTERVAL_TIMEOUT:
|
|
||||||
manager.onFailure(1);
|
|
||||||
break;
|
|
||||||
case BTService.EVENT_TURN_ON_TIMEOUT:
|
|
||||||
manager.onFailure(1);
|
|
||||||
break;
|
|
||||||
case BTService.EVENT_OTHER_ERROR:
|
|
||||||
manager.onFailure(1);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,6 +60,7 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
|||||||
|
|
||||||
private void onConnected() {
|
private void onConnected() {
|
||||||
stateNotify(ConnectionManager.STATE_CONNECTED);
|
stateNotify(ConnectionManager.STATE_CONNECTED);
|
||||||
|
//mInitManager.startTimeoutCountdown(InitManager.DEFAULT_INIT_TIMEOUT);
|
||||||
mInitManager.onConnected();
|
mInitManager.onConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +70,8 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
|||||||
|
|
||||||
private void onFailure(int code) {
|
private void onFailure(int code) {
|
||||||
mErrorCode = code;
|
mErrorCode = code;
|
||||||
stateNotify(ConnectionManager.STATE_FAILURE);
|
stateNotify(ConnectionManager.STATE_FAILURE);
|
||||||
|
disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onData(byte[] rawData) {
|
private void onData(byte[] rawData) {
|
||||||
@ -97,16 +79,15 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
|||||||
data = mPacketManager.bytesToPacket(rawData);
|
data = mPacketManager.bytesToPacket(rawData);
|
||||||
|
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
//TODO
|
//TODO failure?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mInitManager.onData(data);
|
mInitManager.onData(data);
|
||||||
|
|
||||||
//sendNext(); TODO
|
|
||||||
for (InputStickDataListener listener : mDataListeners) {
|
for (InputStickDataListener listener : mDataListeners) {
|
||||||
listener.onInputStickData(data);
|
listener.onInputStickData(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -124,7 +105,7 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
|||||||
|
|
||||||
|
|
||||||
public void connect(boolean reflection, int timeout) {
|
public void connect(boolean reflection, int timeout) {
|
||||||
mErrorCode = ConnectionManager.ERROR_NONE;
|
mErrorCode = InputStickError.ERROR_NONE;
|
||||||
if (mBTService == null) {
|
if (mBTService == null) {
|
||||||
mBTService = new BTService(mApp, mBTHandler);
|
mBTService = new BTService(mApp, mBTHandler);
|
||||||
mPacketManager = new PacketManager(mBTService, mKey);
|
mPacketManager = new PacketManager(mBTService, mKey);
|
||||||
@ -143,10 +124,14 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void disconnect(int failureCode) {
|
||||||
|
onFailure(failureCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendPacket(Packet p) {
|
public void sendPacket(Packet p) {
|
||||||
mPacketManager.sendPacket(p); //TODO tmp; zalozmy z beda same NO_RESP ???
|
mPacketManager.sendPacket(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -162,7 +147,7 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitFailure(int code) {
|
public void onInitFailure(int code) {
|
||||||
onFailure(code);
|
onFailure(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,6 @@ public abstract class ConnectionManager {
|
|||||||
public static final int STATE_READY = 4;
|
public static final int STATE_READY = 4;
|
||||||
|
|
||||||
|
|
||||||
public static final int ERROR_NONE = 0;
|
|
||||||
|
|
||||||
public static final int ERROR_UNSUPPORTED_FIRMWARE = 10;
|
|
||||||
public static final int ERROR_PASSWORD_PROTECTED = 11;
|
|
||||||
public static final int ERROR_INVALID_KEY = 12;
|
|
||||||
|
|
||||||
|
|
||||||
protected Vector<InputStickStateListener> mStateListeners = new Vector<InputStickStateListener>();
|
protected Vector<InputStickStateListener> mStateListeners = new Vector<InputStickStateListener>();
|
||||||
protected Vector<InputStickDataListener> mDataListeners = new Vector<InputStickDataListener>();
|
protected Vector<InputStickDataListener> mDataListeners = new Vector<InputStickDataListener>();
|
||||||
|
|
||||||
@ -47,7 +40,9 @@ public abstract class ConnectionManager {
|
|||||||
|
|
||||||
public void addStateListener(InputStickStateListener listener) {
|
public void addStateListener(InputStickStateListener listener) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
mStateListeners.add(listener);
|
if ( !mStateListeners.contains(listener)) {
|
||||||
|
mStateListeners.add(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +54,9 @@ public abstract class ConnectionManager {
|
|||||||
|
|
||||||
public void addDataListener(InputStickDataListener listener) {
|
public void addDataListener(InputStickDataListener listener) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
mDataListeners.add(listener);
|
if ( !mDataListeners.contains(listener)) {
|
||||||
|
mDataListeners.add(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,10 +15,16 @@ public class HIDInfo {
|
|||||||
private boolean mouseReady;
|
private boolean mouseReady;
|
||||||
private boolean consumerReady;
|
private boolean consumerReady;
|
||||||
|
|
||||||
|
// >= 0.93
|
||||||
|
private boolean sentToHostInfo;
|
||||||
|
private int keyboardReportsSentToHost;
|
||||||
|
private int mouseReportsSentToHost;
|
||||||
|
private int consumerReportsSentToHost;
|
||||||
|
|
||||||
public HIDInfo() {
|
public HIDInfo() {
|
||||||
keyboardReportProtocol = true;
|
keyboardReportProtocol = true;
|
||||||
mouseReportProtocol = true;
|
mouseReportProtocol = true;
|
||||||
|
sentToHostInfo = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(byte[] data) {
|
public void update(byte[] data) {
|
||||||
@ -70,6 +76,14 @@ public class HIDInfo {
|
|||||||
} else {
|
} else {
|
||||||
consumerReady = true;
|
consumerReady = true;
|
||||||
}
|
}
|
||||||
|
if (data.length >= 12) {
|
||||||
|
if (data[11] == (byte)0xFF) {
|
||||||
|
sentToHostInfo = true;
|
||||||
|
keyboardReportsSentToHost = data[8] & 0xFF;
|
||||||
|
mouseReportsSentToHost = data[9] & 0xFF;
|
||||||
|
consumerReportsSentToHost = data[10] & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyboardBusy() {
|
public void setKeyboardBusy() {
|
||||||
@ -112,4 +126,24 @@ public class HIDInfo {
|
|||||||
return consumerReady;
|
return consumerReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// > v0.93 firmware only
|
||||||
|
|
||||||
|
public boolean isSentToHostInfoAvailable() {
|
||||||
|
return sentToHostInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeyboardReportsSentToHost() {
|
||||||
|
return keyboardReportsSentToHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMouseReportsSentToHost() {
|
||||||
|
return mouseReportsSentToHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConsumerReportsSentToHost() {
|
||||||
|
return consumerReportsSentToHost;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,6 @@ public class IPCConnectionManager extends ConnectionManager {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SERVICE_CMD_STATE:
|
case SERVICE_CMD_STATE:
|
||||||
//System.out.println("CMD STATE: "+msg.arg1);
|
|
||||||
manager.stateNotify(msg.arg1);
|
manager.stateNotify(msg.arg1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -61,7 +60,6 @@ public class IPCConnectionManager extends ConnectionManager {
|
|||||||
|
|
||||||
private ServiceConnection mConnection = new ServiceConnection() {
|
private ServiceConnection mConnection = new ServiceConnection() {
|
||||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||||
//System.out.println("onServiceConnected!");
|
|
||||||
mService = new Messenger(service);
|
mService = new Messenger(service);
|
||||||
mBound = true;
|
mBound = true;
|
||||||
sendMessage(SERVICE_CMD_CONNECT, 0, 0);
|
sendMessage(SERVICE_CMD_CONNECT, 0, 0);
|
||||||
@ -69,9 +67,10 @@ public class IPCConnectionManager extends ConnectionManager {
|
|||||||
|
|
||||||
public void onServiceDisconnected(ComponentName className) {
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
// unexpectedly disconnected from service
|
// unexpectedly disconnected from service
|
||||||
//System.out.println("onService DISCONNECTED!");
|
|
||||||
mService = null;
|
mService = null;
|
||||||
mBound = false;
|
mBound = false;
|
||||||
|
mErrorCode = InputStickError.ERROR_ANDROID_SERVICE_DISCONNECTED;
|
||||||
|
stateNotify(STATE_FAILURE);
|
||||||
stateNotify(STATE_DISCONNECTED);
|
stateNotify(STATE_DISCONNECTED);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -130,34 +129,32 @@ public class IPCConnectionManager extends ConnectionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (exists) {
|
if (exists) {
|
||||||
mErrorCode = ConnectionManager.ERROR_NONE;
|
mErrorCode = InputStickError.ERROR_NONE;
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setComponent(new ComponentName("com.inputstick.apps.inputstickutility","com.inputstick.apps.inputstickutility.service.InputStickService"));
|
intent.setComponent(new ComponentName("com.inputstick.apps.inputstickutility","com.inputstick.apps.inputstickutility.service.InputStickService"));
|
||||||
mCtx.startService(intent);
|
mCtx.startService(intent);
|
||||||
mCtx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
|
mCtx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
|
||||||
if (mBound) {
|
if (mBound) {
|
||||||
//already bound?
|
//already bound
|
||||||
//System.out.println("Service already Connected");
|
|
||||||
sendMessage(SERVICE_CMD_CONNECT, 0, 0);
|
sendMessage(SERVICE_CMD_CONNECT, 0, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mErrorCode = 1; //TODO
|
mErrorCode = InputStickError.ERROR_ANDROID_NO_UTILITY_APP;
|
||||||
stateNotify(STATE_FAILURE);
|
stateNotify(STATE_FAILURE);
|
||||||
|
stateNotify(STATE_DISCONNECTED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
if (mBound) {
|
if (mBound) {
|
||||||
//System.out.println("UNBIND");
|
|
||||||
sendMessage(SERVICE_CMD_DISCONNECT, 0, 0);
|
sendMessage(SERVICE_CMD_DISCONNECT, 0, 0);
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setComponent(new ComponentName("com.inputstick.apps.inputstickutility","com.inputstick.apps.inputstickutility.service.InputStickService"));
|
intent.setComponent(new ComponentName("com.inputstick.apps.inputstickutility","com.inputstick.apps.inputstickutility.service.InputStickService"));
|
||||||
mCtx.unbindService(mConnection);
|
mCtx.unbindService(mConnection);
|
||||||
mCtx.stopService(intent);
|
mCtx.stopService(intent);
|
||||||
mBound = false;
|
mBound = false;
|
||||||
//TODO stateNotify
|
//service will pass notification message (disconnected)
|
||||||
//service will pass notification message
|
|
||||||
} else {
|
} else {
|
||||||
//just set state, there is nothing else to do
|
//just set state, there is nothing else to do
|
||||||
stateNotify(STATE_DISCONNECTED);
|
stateNotify(STATE_DISCONNECTED);
|
||||||
@ -172,9 +169,8 @@ public class IPCConnectionManager extends ConnectionManager {
|
|||||||
} else {
|
} else {
|
||||||
sendMessage(IPCConnectionManager.SERVICE_CMD_DATA, 0, 0, p.getBytes());
|
sendMessage(IPCConnectionManager.SERVICE_CMD_DATA, 0, 0, p.getBytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,12 @@ public class Packet {
|
|||||||
|
|
||||||
public static final byte CMD_FW_INFO = 0x10;
|
public static final byte CMD_FW_INFO = 0x10;
|
||||||
public static final byte CMD_INIT = 0x11;
|
public static final byte CMD_INIT = 0x11;
|
||||||
|
public static final byte CMD_INIT_AUTH = 0x12;
|
||||||
|
public static final byte CMD_INIT_CON = 0x13;
|
||||||
|
//public static final byte CMD_SET_KEY = 0x14;
|
||||||
|
public static final byte CMD_SET_VALUE = 0x14;
|
||||||
|
public static final byte CMD_RESTORE_DEFAULTS = 0x15;
|
||||||
|
public static final byte CMD_RESTORE_STATUS = 0x16;
|
||||||
|
|
||||||
|
|
||||||
public static final byte CMD_HID_STATUS_REPORT = 0x20;
|
public static final byte CMD_HID_STATUS_REPORT = 0x20;
|
||||||
@ -37,6 +43,7 @@ public class Packet {
|
|||||||
|
|
||||||
|
|
||||||
public static final byte RESP_OK = 0x01;
|
public static final byte RESP_OK = 0x01;
|
||||||
|
public static final byte RESP_UNKNOWN_CMD = (byte)0xFF;
|
||||||
|
|
||||||
|
|
||||||
public static final byte[] RAW_OLD_BOOTLOADER = new byte[] {START_TAG, (byte)0x00, (byte)0x02, (byte)0x83, (byte)0x00, (byte)0xDA};
|
public static final byte[] RAW_OLD_BOOTLOADER = new byte[] {START_TAG, (byte)0x00, (byte)0x02, (byte)0x83, (byte)0x00, (byte)0xDA};
|
||||||
@ -119,4 +126,8 @@ public class Packet {
|
|||||||
return mRespond;
|
return mRespond;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void print() {
|
||||||
|
Util.printHex(mData, "PACKET DATA:");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,13 @@ public class PacketManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEncrypted() {
|
||||||
|
return mEncryption;
|
||||||
|
}
|
||||||
|
|
||||||
public Packet encPacket(boolean enable) {
|
public Packet encPacket(boolean enable) {
|
||||||
Random r = new Random();
|
Random r = new Random();
|
||||||
Packet p = new Packet(true, Packet.CMD_INIT);
|
Packet p = new Packet(true, Packet.CMD_INIT_AUTH);
|
||||||
if (enable) {
|
if (enable) {
|
||||||
p.addByte((byte)1);
|
p.addByte((byte)1);
|
||||||
} else {
|
} else {
|
||||||
@ -68,13 +72,13 @@ public class PacketManager {
|
|||||||
initData = mAes.encrypt(initData);
|
initData = mAes.encrypt(initData);
|
||||||
p.addBytes(initData);
|
p.addBytes(initData);
|
||||||
|
|
||||||
//Util.printHex(initData, "InitData: ");
|
Util.printHex(initData, "InitData: ");
|
||||||
|
|
||||||
cmpData = new byte[16];
|
cmpData = new byte[16];
|
||||||
r.nextBytes(cmpData);
|
r.nextBytes(cmpData);
|
||||||
p.addBytes(cmpData);
|
p.addBytes(cmpData);
|
||||||
|
|
||||||
//Util.printHex(cmpData, "CmpData: ");
|
Util.printHex(cmpData, "CmpData: ");
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,11 +91,15 @@ public class PacketManager {
|
|||||||
|
|
||||||
payload = Arrays.copyOfRange(data, 2, data.length); //remove TAG, info
|
payload = Arrays.copyOfRange(data, 2, data.length); //remove TAG, info
|
||||||
if ((data[1] & Packet.FLAG_ENCRYPTED) != 0) {
|
if ((data[1] & Packet.FLAG_ENCRYPTED) != 0) {
|
||||||
Util.log("DECRYPT");
|
//Util.log("DECRYPT");
|
||||||
payload = mAes.decrypt(payload);
|
if (mAes.isReady()) {
|
||||||
|
payload = mAes.decrypt(payload);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Util.printHex(payload, "DATA IN: ");
|
//Util.printHex(payload, "DATA IN: ");
|
||||||
|
|
||||||
//check CRC
|
//check CRC
|
||||||
crcCompare = Util.getLong(payload[0], payload[1], payload[2], payload[3]);
|
crcCompare = Util.getLong(payload[0], payload[1], payload[2], payload[3]);
|
||||||
@ -104,17 +112,19 @@ public class PacketManager {
|
|||||||
payload = Arrays.copyOfRange(payload, 4, payload.length); //remove CRC
|
payload = Arrays.copyOfRange(payload, 4, payload.length); //remove CRC
|
||||||
return payload;
|
return payload;
|
||||||
} else {
|
} else {
|
||||||
return null; //TODO
|
return null; //TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendRAW(byte[] data) {
|
public void sendRAW(byte[] data) {
|
||||||
mBTService.write(data);
|
mBTService.write(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacket(Packet p) {
|
public void sendPacket(Packet p) {
|
||||||
sendPacket(p, mEncryption);
|
if (p != null) {
|
||||||
|
sendPacket(p, mEncryption);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacket(Packet p, boolean encrypt) {
|
public void sendPacket(Packet p, boolean encrypt) {
|
||||||
@ -137,7 +147,7 @@ public class PacketManager {
|
|||||||
mCrc.reset();
|
mCrc.reset();
|
||||||
mCrc.update(result, CRC_OFFSET, result.length - CRC_OFFSET);
|
mCrc.update(result, CRC_OFFSET, result.length - CRC_OFFSET);
|
||||||
crcValue = mCrc.getValue();
|
crcValue = mCrc.getValue();
|
||||||
Util.log("CRC: "+crcValue);
|
//Util.log("CRC: "+crcValue);
|
||||||
result[3] = (byte)crcValue;
|
result[3] = (byte)crcValue;
|
||||||
crcValue >>= 8;
|
crcValue >>= 8;
|
||||||
result[2] = (byte)crcValue;
|
result[2] = (byte)crcValue;
|
||||||
|
@ -1,13 +1,25 @@
|
|||||||
package com.inputstick.api;
|
package com.inputstick.api;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
|
||||||
public abstract class Util {
|
public abstract class Util {
|
||||||
|
|
||||||
private static final boolean debug = false;
|
public static boolean debug = false;
|
||||||
|
|
||||||
public static void log(String msg) {
|
public static void log(String msg) {
|
||||||
|
log(msg, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void log(String msg, boolean displayTime) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
System.out.println("LOG: " + msg);
|
System.out.print("LOG: " + msg);
|
||||||
|
if (displayTime) {
|
||||||
|
System.out.print(" @ " + System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,29 +33,35 @@ public abstract class Util {
|
|||||||
|
|
||||||
public static void printHex(byte[] toPrint) {
|
public static void printHex(byte[] toPrint) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
int cnt = 0;
|
if (toPrint != null) {
|
||||||
String s;
|
int cnt = 0;
|
||||||
byte b;
|
String s;
|
||||||
for (int i = 0; i < toPrint.length; i++) {
|
byte b;
|
||||||
b = toPrint[i];
|
for (int i = 0; i < toPrint.length; i++) {
|
||||||
if ((b < 10) && (b >= 0)) {
|
b = toPrint[i];
|
||||||
s = Integer.toHexString((int)b);
|
//0x0..0xF = 0x00..0x0F
|
||||||
s = "0" + s;
|
if ((b < 0x10) && (b >= 0)) {
|
||||||
} else {
|
s = Integer.toHexString((int)b);
|
||||||
s = Integer.toHexString((int)b);
|
s = "0" + s;
|
||||||
if (s.length() > 2) {
|
} else {
|
||||||
s = s.substring(s.length() - 2);
|
s = Integer.toHexString((int)b);
|
||||||
|
if (s.length() > 2) {
|
||||||
|
s = s.substring(s.length() - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = s.toUpperCase();
|
||||||
|
System.out.print("0x" + s + " ");
|
||||||
|
cnt++;
|
||||||
|
if (cnt == 8) {
|
||||||
|
System.out.println("");
|
||||||
|
cnt = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s = s.toUpperCase();
|
|
||||||
System.out.print("0x" + s + " ");
|
} else {
|
||||||
cnt++;
|
System.out.println("null");
|
||||||
if (cnt == 8) {
|
}
|
||||||
System.out.println("");
|
System.out.println("\n#####");
|
||||||
cnt = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.out.println("\n#####");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,5 +97,18 @@ public abstract class Util {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] getPasswordBytes(String plainText) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
return md.digest(plainText.getBytes("UTF-8"));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,54 @@
|
|||||||
package com.inputstick.api.basic;
|
package com.inputstick.api.basic;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
import com.inputstick.api.BTConnectionManager;
|
import com.inputstick.api.BTConnectionManager;
|
||||||
import com.inputstick.api.ConnectionManager;
|
import com.inputstick.api.ConnectionManager;
|
||||||
import com.inputstick.api.HIDInfo;
|
import com.inputstick.api.HIDInfo;
|
||||||
import com.inputstick.api.IPCConnectionManager;
|
import com.inputstick.api.IPCConnectionManager;
|
||||||
import com.inputstick.api.InputStickDataListener;
|
import com.inputstick.api.InputStickDataListener;
|
||||||
|
import com.inputstick.api.InputStickError;
|
||||||
import com.inputstick.api.InputStickStateListener;
|
import com.inputstick.api.InputStickStateListener;
|
||||||
|
import com.inputstick.api.OnEmptyBufferListener;
|
||||||
import com.inputstick.api.Packet;
|
import com.inputstick.api.Packet;
|
||||||
|
import com.inputstick.api.Util;
|
||||||
import com.inputstick.api.hid.HIDTransaction;
|
import com.inputstick.api.hid.HIDTransaction;
|
||||||
import com.inputstick.api.hid.HIDTransactionQueue;
|
import com.inputstick.api.hid.HIDTransactionQueue;
|
||||||
import com.inputstick.init.InitManager;
|
import com.inputstick.init.InitManager;
|
||||||
|
|
||||||
public class InputStickHID implements InputStickStateListener, InputStickDataListener {
|
public class InputStickHID implements InputStickStateListener, InputStickDataListener {
|
||||||
|
|
||||||
//private static final String mTag = "InputStickBasic";
|
public static final int INTERFACE_KEYBOARD = 0;
|
||||||
|
public static final int INTERFACE_CONSUMER = 1;
|
||||||
|
public static final int INTERFACE_MOUSE = 2;
|
||||||
|
|
||||||
|
|
||||||
|
//private static final String mTag = "InputStickBasic";
|
||||||
private static ConnectionManager mConnectionManager;
|
private static ConnectionManager mConnectionManager;
|
||||||
|
|
||||||
private static Vector<InputStickStateListener> mStateListeners = new Vector<InputStickStateListener>();
|
private static Vector<InputStickStateListener> mStateListeners = new Vector<InputStickStateListener>();
|
||||||
|
|
||||||
|
|
||||||
private static InputStickHID instance = new InputStickHID();
|
private static InputStickHID instance = new InputStickHID();
|
||||||
private static HIDInfo mHIDInfo = new HIDInfo();
|
private static HIDInfo mHIDInfo;
|
||||||
|
|
||||||
private static HIDTransactionQueue keyboardQueue;
|
private static HIDTransactionQueue keyboardQueue;
|
||||||
private static HIDTransactionQueue mouseQueue;
|
private static HIDTransactionQueue mouseQueue;
|
||||||
private static HIDTransactionQueue consumerQueue;
|
private static HIDTransactionQueue consumerQueue;
|
||||||
|
|
||||||
|
// >= FW 0.93
|
||||||
|
private static Timer t1;
|
||||||
|
private static boolean constantUpdateMode;
|
||||||
|
|
||||||
private InputStickHID() {
|
private InputStickHID() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,9 +57,11 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void init() {
|
private static void init() {
|
||||||
keyboardQueue = new HIDTransactionQueue(HIDTransactionQueue.KEYBOARD, mConnectionManager);
|
mHIDInfo = new HIDInfo();
|
||||||
mouseQueue = new HIDTransactionQueue(HIDTransactionQueue.MOUSE, mConnectionManager);
|
constantUpdateMode = false;
|
||||||
consumerQueue = new HIDTransactionQueue(HIDTransactionQueue.CONSUMER, mConnectionManager);
|
keyboardQueue = new HIDTransactionQueue(INTERFACE_KEYBOARD, mConnectionManager);
|
||||||
|
mouseQueue = new HIDTransactionQueue(INTERFACE_MOUSE, mConnectionManager);
|
||||||
|
consumerQueue = new HIDTransactionQueue(INTERFACE_CONSUMER, mConnectionManager);
|
||||||
|
|
||||||
mConnectionManager.addStateListener(instance);
|
mConnectionManager.addStateListener(instance);
|
||||||
mConnectionManager.addDataListener(instance);
|
mConnectionManager.addDataListener(instance);
|
||||||
@ -55,8 +76,6 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
|
|||||||
|
|
||||||
//direct Bluetooth connection
|
//direct Bluetooth connection
|
||||||
public static void connect(Application app, String mac, byte[] key) {
|
public static void connect(Application app, String mac, byte[] key) {
|
||||||
//mConnectionManager = new BTConnectionManager(new BasicInitManager(key), app, mac, reflections, key);
|
|
||||||
//mConnectionManager = new BTConnectionManager(new BasicInitManager(key), app, mac, key);
|
|
||||||
mConnectionManager = new BTConnectionManager(new InitManager(key), app, mac, key);
|
mConnectionManager = new BTConnectionManager(new InitManager(key), app, mac, key);
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@ -80,6 +99,16 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getErrorCode() {
|
||||||
|
if (mConnectionManager != null) {
|
||||||
|
return mConnectionManager.getErrorCode();
|
||||||
|
} else {
|
||||||
|
return InputStickError.ERROR_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static boolean isReady() {
|
public static boolean isReady() {
|
||||||
if (getState() == ConnectionManager.STATE_READY) {
|
if (getState() == ConnectionManager.STATE_READY) {
|
||||||
return true;
|
return true;
|
||||||
@ -90,7 +119,9 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
|
|||||||
|
|
||||||
public static void addStateListener(InputStickStateListener listener) {
|
public static void addStateListener(InputStickStateListener listener) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
mStateListeners.add(listener);
|
if ( !mStateListeners.contains(listener)) {
|
||||||
|
mStateListeners.add(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +130,22 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
|
|||||||
mStateListeners.remove(listener);
|
mStateListeners.remove(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addBufferEmptyListener(OnEmptyBufferListener listener) {
|
||||||
|
if (listener != null) {
|
||||||
|
keyboardQueue.addBufferEmptyListener(listener);
|
||||||
|
mouseQueue.addBufferEmptyListener(listener);
|
||||||
|
consumerQueue.addBufferEmptyListener(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeBufferEmptyListener(OnEmptyBufferListener listener) {
|
||||||
|
if (listener != null) {
|
||||||
|
keyboardQueue.removeBufferEmptyListener(listener);
|
||||||
|
mouseQueue.removeBufferEmptyListener(listener);
|
||||||
|
consumerQueue.removeBufferEmptyListener(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void addKeyboardTransaction(HIDTransaction transaction) {
|
public static void addKeyboardTransaction(HIDTransaction transaction) {
|
||||||
keyboardQueue.addTransaction(transaction);
|
keyboardQueue.addTransaction(transaction);
|
||||||
@ -112,6 +159,18 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
|
|||||||
consumerQueue.addTransaction(transaction);
|
consumerQueue.addTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void clearKeyboardBuffer() {
|
||||||
|
keyboardQueue.clearBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clearMouseBuffer() {
|
||||||
|
mouseQueue.clearBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clearConsumerBuffer() {
|
||||||
|
consumerQueue.clearBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean sendPacket(Packet p) {
|
public static boolean sendPacket(Packet p) {
|
||||||
if (mConnectionManager != null) {
|
if (mConnectionManager != null) {
|
||||||
mConnectionManager.sendPacket(p);
|
mConnectionManager.sendPacket(p);
|
||||||
@ -122,31 +181,112 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStateChanged(int state) {
|
public void onStateChanged(int state) {
|
||||||
|
if ((state == ConnectionManager.STATE_DISCONNECTED) && (t1 != null)) {
|
||||||
|
t1.cancel();
|
||||||
|
t1 = null;
|
||||||
|
}
|
||||||
for (InputStickStateListener listener : mStateListeners) {
|
for (InputStickStateListener listener : mStateListeners) {
|
||||||
listener.onStateChanged(state);
|
listener.onStateChanged(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isKeyboardLocalBufferEmpty() {
|
||||||
|
return keyboardQueue.isLocalBufferEmpty();
|
||||||
|
}
|
||||||
|
public static boolean isMouseLocalBufferEmpty() {
|
||||||
|
return mouseQueue.isLocalBufferEmpty();
|
||||||
|
}
|
||||||
|
public static boolean isConsumerLocalBufferEmpty() {
|
||||||
|
return consumerQueue.isLocalBufferEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isKeyboardRemoteBufferEmpty() {
|
||||||
|
return keyboardQueue.isRemoteBufferEmpty();
|
||||||
|
}
|
||||||
|
public static boolean isMouseRemoteBufferEmpty() {
|
||||||
|
return mouseQueue.isRemoteBufferEmpty();
|
||||||
|
}
|
||||||
|
public static boolean isConsumerRemoteBufferEmpty() {
|
||||||
|
return consumerQueue.isRemoteBufferEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInputStickData(byte[] data) {
|
public void onInputStickData(byte[] data) {
|
||||||
if (data[0] == Packet.CMD_HID_STATUS) {
|
if (data[0] == Packet.CMD_HID_STATUS) {
|
||||||
mHIDInfo.update(data);
|
mHIDInfo.update(data);
|
||||||
|
|
||||||
if (mHIDInfo.isKeyboardReady()) {
|
if (mHIDInfo.isSentToHostInfoAvailable()) {
|
||||||
keyboardQueue.deviceReady();
|
// >= FW 0.93
|
||||||
|
keyboardQueue.deviceReady(mHIDInfo, mHIDInfo.getKeyboardReportsSentToHost());
|
||||||
|
mouseQueue.deviceReady(mHIDInfo, mHIDInfo.getMouseReportsSentToHost());
|
||||||
|
consumerQueue.deviceReady(mHIDInfo, mHIDInfo.getConsumerReportsSentToHost());
|
||||||
|
|
||||||
|
if ( !constantUpdateMode) {
|
||||||
|
Util.log("Constatnt update mode enabled");
|
||||||
|
constantUpdateMode = true;
|
||||||
|
t1 = new Timer();
|
||||||
|
t1.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
keyboardQueue.sendToBuffer(false);
|
||||||
|
mouseQueue.sendToBuffer(false);
|
||||||
|
consumerQueue.sendToBuffer(false);
|
||||||
|
}
|
||||||
|
}, 5,5);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//previous FW versions
|
||||||
|
if (mHIDInfo.isKeyboardReady()) {
|
||||||
|
keyboardQueue.deviceReady(null, 0);
|
||||||
|
}
|
||||||
|
if (mHIDInfo.isMouseReady()) {
|
||||||
|
mouseQueue.deviceReady(null, 0);
|
||||||
|
}
|
||||||
|
if (mHIDInfo.isConsumerReady()) {
|
||||||
|
consumerQueue.deviceReady(null, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mHIDInfo.isMouseReady()) {
|
|
||||||
mouseQueue.deviceReady();
|
|
||||||
}
|
|
||||||
if (mHIDInfo.isConsumerReady()) {
|
|
||||||
consumerQueue.deviceReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStickKeyboard.setLEDs(mHIDInfo.getNumLock(), mHIDInfo.getCapsLock(), mHIDInfo.getScrollLock());
|
InputStickKeyboard.setLEDs(mHIDInfo.getNumLock(), mHIDInfo.getCapsLock(), mHIDInfo.getScrollLock());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static AlertDialog getDownloadDialog(final Context ctx) {
|
||||||
|
if (mConnectionManager.getErrorCode() == InputStickError.ERROR_ANDROID_NO_UTILITY_APP) {
|
||||||
|
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(ctx);
|
||||||
|
downloadDialog.setTitle("No InputStickUtility app installed");
|
||||||
|
downloadDialog.setMessage("InputStickUtility is required to run this application. Download now?");
|
||||||
|
downloadDialog.setPositiveButton("Yes",
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
final String appPackageName = "com.inputstick.apps.inputstickutility";
|
||||||
|
try {
|
||||||
|
ctx.startActivity(new Intent(
|
||||||
|
Intent.ACTION_VIEW, Uri
|
||||||
|
.parse("market://details?id="
|
||||||
|
+ appPackageName)));
|
||||||
|
} catch (android.content.ActivityNotFoundException anfe) {
|
||||||
|
ctx.startActivity(new Intent(
|
||||||
|
Intent.ACTION_VIEW,
|
||||||
|
Uri.parse("http://play.google.com/store/apps/details?id="
|
||||||
|
+ appPackageName)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
downloadDialog.setNegativeButton("No",
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return downloadDialog.show();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ package com.inputstick.api.basic;
|
|||||||
|
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.inputstick.api.InputStickKeyboardListener;
|
import com.inputstick.api.InputStickKeyboardListener;
|
||||||
import com.inputstick.api.hid.HIDKeycodes;
|
import com.inputstick.api.hid.HIDKeycodes;
|
||||||
import com.inputstick.api.hid.HIDTransaction;
|
import com.inputstick.api.hid.HIDTransaction;
|
||||||
@ -13,6 +15,10 @@ public class InputStickKeyboard {
|
|||||||
|
|
||||||
private static final byte NONE = (byte)0;
|
private static final byte NONE = (byte)0;
|
||||||
|
|
||||||
|
private static final byte LED_NUM_LOCK = 1;
|
||||||
|
private static final byte LED_CAPS_LOCK = 2;
|
||||||
|
private static final byte LED_SCROLL_LOCK = 4;
|
||||||
|
|
||||||
private static boolean mReportProtocol;
|
private static boolean mReportProtocol;
|
||||||
private static boolean mNumLock;
|
private static boolean mNumLock;
|
||||||
private static boolean mCapsLock;
|
private static boolean mCapsLock;
|
||||||
@ -20,12 +26,23 @@ public class InputStickKeyboard {
|
|||||||
|
|
||||||
private static Vector<InputStickKeyboardListener> mKeyboardListeners = new Vector<InputStickKeyboardListener>();
|
private static Vector<InputStickKeyboardListener> mKeyboardListeners = new Vector<InputStickKeyboardListener>();
|
||||||
|
|
||||||
|
private static final SparseArray<String> ledsMap;
|
||||||
|
static
|
||||||
|
{
|
||||||
|
ledsMap = new SparseArray<String>();
|
||||||
|
ledsMap.put(LED_NUM_LOCK, "NumLock");
|
||||||
|
ledsMap.put(LED_CAPS_LOCK, "CapsLock");
|
||||||
|
ledsMap.put(LED_SCROLL_LOCK, "ScrollLock");
|
||||||
|
}
|
||||||
|
|
||||||
private InputStickKeyboard() {
|
private InputStickKeyboard() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addKeyboardListener(InputStickKeyboardListener listener) {
|
public static void addKeyboardListener(InputStickKeyboardListener listener) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
mKeyboardListeners.add(listener);
|
if ( !mKeyboardListeners.contains(listener)) {
|
||||||
|
mKeyboardListeners.add(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,5 +131,25 @@ public class InputStickKeyboard {
|
|||||||
t.addReport(report);
|
t.addReport(report);
|
||||||
InputStickHID.addKeyboardTransaction(t);
|
InputStickHID.addKeyboardTransaction(t);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
public static String ledsToString(byte leds) {
|
||||||
|
String result = "None";
|
||||||
|
boolean first = true;
|
||||||
|
byte mod;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
mod = (byte)(LED_NUM_LOCK << i);
|
||||||
|
if ((leds & mod) != 0) {
|
||||||
|
if ( !first) {
|
||||||
|
result += ", ";
|
||||||
|
} else {
|
||||||
|
result = "";
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
result += ledsMap.get(mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.inputstick.api.basic;
|
package com.inputstick.api.basic;
|
||||||
|
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.inputstick.api.hid.HIDTransaction;
|
import com.inputstick.api.hid.HIDTransaction;
|
||||||
import com.inputstick.api.hid.MouseReport;
|
import com.inputstick.api.hid.MouseReport;
|
||||||
|
|
||||||
@ -12,6 +14,15 @@ public class InputStickMouse {
|
|||||||
public static final byte BUTTON_RIGHT = 0x02;
|
public static final byte BUTTON_RIGHT = 0x02;
|
||||||
public static final byte BUTTON_MIDDLE = 0x04;
|
public static final byte BUTTON_MIDDLE = 0x04;
|
||||||
|
|
||||||
|
private static final SparseArray<String> buttonsMap;
|
||||||
|
static
|
||||||
|
{
|
||||||
|
buttonsMap = new SparseArray<String>();
|
||||||
|
buttonsMap.put(BUTTON_LEFT, "Left");
|
||||||
|
buttonsMap.put(BUTTON_RIGHT, "Right");
|
||||||
|
buttonsMap.put(BUTTON_MIDDLE, "Middle");
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean mReportProtocol;
|
private static boolean mReportProtocol;
|
||||||
|
|
||||||
private InputStickMouse() {
|
private InputStickMouse() {
|
||||||
@ -53,5 +64,25 @@ public class InputStickMouse {
|
|||||||
t.addReport(new MouseReport(buttons, x, y, wheel));
|
t.addReport(new MouseReport(buttons, x, y, wheel));
|
||||||
InputStickHID.addMouseTransaction(t);
|
InputStickHID.addMouseTransaction(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String buttonsToString(byte buttons) {
|
||||||
|
String result = "None";
|
||||||
|
boolean first = true;
|
||||||
|
byte mod;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
mod = (byte)(BUTTON_LEFT << i);
|
||||||
|
if ((buttons & mod) != 0) {
|
||||||
|
if ( !first) {
|
||||||
|
result += ", ";
|
||||||
|
} else {
|
||||||
|
result = "";
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
result += buttonsMap.get(mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import android.os.Message;
|
|||||||
|
|
||||||
import com.inputstick.api.Packet;
|
import com.inputstick.api.Packet;
|
||||||
import com.inputstick.api.Util;
|
import com.inputstick.api.Util;
|
||||||
|
import com.inputstick.api.InputStickError;
|
||||||
|
|
||||||
public class BTService {
|
public class BTService {
|
||||||
|
|
||||||
@ -28,16 +29,7 @@ public class BTService {
|
|||||||
public static final int EVENT_DATA = 1;
|
public static final int EVENT_DATA = 1;
|
||||||
public static final int EVENT_CONNECTED = 2;
|
public static final int EVENT_CONNECTED = 2;
|
||||||
public static final int EVENT_CANCELLED = 3;
|
public static final int EVENT_CANCELLED = 3;
|
||||||
public static final int EVENT_CONNECTION_FAILED = 4;
|
public static final int EVENT_ERROR = 4;
|
||||||
public static final int EVENT_CONNECTION_LOST = 5;
|
|
||||||
public static final int EVENT_NO_BT_HW = 6;
|
|
||||||
public static final int EVENT_INVALID_MAC = 7;
|
|
||||||
//TODO:
|
|
||||||
public static final int EVENT_CMD_TIMEOUT = 8;
|
|
||||||
public static final int EVENT_INTERVAL_TIMEOUT = 9;
|
|
||||||
public static final int EVENT_TURN_ON_TIMEOUT = 10;
|
|
||||||
public static final int EVENT_OTHER_ERROR = 11;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //SPP
|
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //SPP
|
||||||
@ -71,8 +63,7 @@ public class BTService {
|
|||||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
final String action = intent.getAction();
|
final String action = intent.getAction();
|
||||||
System.out.println("ACTION: "+action);
|
|
||||||
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
|
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
|
||||||
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
|
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
|
||||||
if ((state == BluetoothAdapter.STATE_ON) && (turnBluetoothOn)) {
|
if ((state == BluetoothAdapter.STATE_ON) && (turnBluetoothOn)) {
|
||||||
@ -102,12 +93,12 @@ public class BTService {
|
|||||||
public void enableReflection(boolean enabled) {
|
public void enableReflection(boolean enabled) {
|
||||||
mUseReflection = enabled;
|
mUseReflection = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void event(int event) {
|
|
||||||
|
private synchronized void event(int event, int arg1) {
|
||||||
Util.log("event() " + mLastEvent + " -> " + event);
|
Util.log("event() " + mLastEvent + " -> " + event);
|
||||||
mLastEvent = event;
|
mLastEvent = event;
|
||||||
|
Message msg = Message.obtain(null, mLastEvent, arg1, 0);
|
||||||
Message msg = Message.obtain(null, mLastEvent, 0, 0);
|
|
||||||
mHandler.sendMessage(msg);
|
mHandler.sendMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +154,7 @@ public class BTService {
|
|||||||
if (BluetoothAdapter.checkBluetoothAddress(mac)) {
|
if (BluetoothAdapter.checkBluetoothAddress(mac)) {
|
||||||
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
if (mBluetoothAdapter == null) {
|
if (mBluetoothAdapter == null) {
|
||||||
event(EVENT_NO_BT_HW);
|
event(EVENT_ERROR, InputStickError.ERROR_BLUETOOTH_NOT_SUPPORTED);
|
||||||
} else {
|
} else {
|
||||||
if (mBluetoothAdapter.isEnabled()) {
|
if (mBluetoothAdapter.isEnabled()) {
|
||||||
doConnect(false);
|
doConnect(false);
|
||||||
@ -172,7 +163,7 @@ public class BTService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
event(EVENT_INVALID_MAC);
|
event(EVENT_ERROR, InputStickError.ERROR_BLUETOOTH_INVALID_MAC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +171,7 @@ public class BTService {
|
|||||||
Util.log("disconnect");
|
Util.log("disconnect");
|
||||||
disconnecting = true;
|
disconnecting = true;
|
||||||
cancelThreads();
|
cancelThreads();
|
||||||
event(EVENT_CANCELLED);
|
event(EVENT_CANCELLED, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -226,7 +217,7 @@ public class BTService {
|
|||||||
mConnectedThread.start();
|
mConnectedThread.start();
|
||||||
|
|
||||||
connected = true;
|
connected = true;
|
||||||
event(EVENT_CONNECTED);
|
event(EVENT_CONNECTED, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -239,7 +230,7 @@ public class BTService {
|
|||||||
Util.log("RETRY: "+retryCnt + " time left: " + (timeout - System.currentTimeMillis()));
|
Util.log("RETRY: "+retryCnt + " time left: " + (timeout - System.currentTimeMillis()));
|
||||||
doConnect(true);
|
doConnect(true);
|
||||||
} else {
|
} else {
|
||||||
event(EVENT_CONNECTION_FAILED);
|
event(EVENT_ERROR, InputStickError.ERROR_BLUETOOTH_CONNECTION_FAILED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,7 +240,7 @@ public class BTService {
|
|||||||
if (disconnecting) {
|
if (disconnecting) {
|
||||||
disconnecting = false;
|
disconnecting = false;
|
||||||
} else {
|
} else {
|
||||||
event(EVENT_CONNECTION_LOST);
|
event(EVENT_ERROR, InputStickError.ERROR_BLUETOOTH_CONNECTION_LOST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,11 +347,13 @@ public class BTService {
|
|||||||
byte[] buffer = null;
|
byte[] buffer = null;
|
||||||
int rxTmp;
|
int rxTmp;
|
||||||
int lengthByte;
|
int lengthByte;
|
||||||
int length;
|
int length;
|
||||||
|
int wdgCnt = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
rxTmp = mmInStream.read();
|
rxTmp = mmInStream.read();
|
||||||
if (rxTmp == Packet.START_TAG) {
|
if (rxTmp == Packet.START_TAG) {
|
||||||
|
wdgCnt = 0;
|
||||||
lengthByte = mmInStream.read();
|
lengthByte = mmInStream.read();
|
||||||
length = lengthByte;
|
length = lengthByte;
|
||||||
length &= 0x3F;
|
length &= 0x3F;
|
||||||
@ -373,8 +366,13 @@ public class BTService {
|
|||||||
}
|
}
|
||||||
mHandler.obtainMessage(EVENT_DATA, 0, 0, buffer).sendToTarget();
|
mHandler.obtainMessage(EVENT_DATA, 0, 0, buffer).sendToTarget();
|
||||||
} else {
|
} else {
|
||||||
System.out.println("RX: " + rxTmp);
|
Util.log("Unexpected RX byte" + rxTmp);
|
||||||
//possible WDG reset
|
if (rxTmp == 0xAF) {
|
||||||
|
wdgCnt++;
|
||||||
|
}
|
||||||
|
if (wdgCnt > 1024) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
connectionLost();
|
connectionLost();
|
||||||
@ -388,7 +386,7 @@ public class BTService {
|
|||||||
mmOutStream.write(buffer);
|
mmOutStream.write(buffer);
|
||||||
mmOutStream.flush();
|
mmOutStream.flush();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Util.log("Exception during write");
|
Util.log("write() exception");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +394,7 @@ public class BTService {
|
|||||||
try {
|
try {
|
||||||
mmSocket.close();
|
mmSocket.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Util.log("close() of connect socket failed");
|
Util.log("socket close() exception");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.inputstick.api.hid;
|
package com.inputstick.api.hid;
|
||||||
|
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
public class HIDKeycodes {
|
public class HIDKeycodes {
|
||||||
|
|
||||||
public static final byte NONE = 0x00;
|
public static final byte NONE = 0x00;
|
||||||
@ -13,6 +15,7 @@ public class HIDKeycodes {
|
|||||||
public static final byte ALT_RIGHT = 0x40;
|
public static final byte ALT_RIGHT = 0x40;
|
||||||
public static final byte GUI_RIGHT = (byte)0x80;
|
public static final byte GUI_RIGHT = (byte)0x80;
|
||||||
|
|
||||||
|
|
||||||
public static final byte KEY_ENTER = 0x28;
|
public static final byte KEY_ENTER = 0x28;
|
||||||
public static final byte KEY_ESCAPE = 0x29;
|
public static final byte KEY_ESCAPE = 0x29;
|
||||||
public static final byte KEY_BACKSPACE = 0x2A;
|
public static final byte KEY_BACKSPACE = 0x2A;
|
||||||
@ -79,6 +82,8 @@ public class HIDKeycodes {
|
|||||||
public static final byte KEY_NUM_0 = 0x62;
|
public static final byte KEY_NUM_0 = 0x62;
|
||||||
public static final byte KEY_NUM_DOT = 0x63;
|
public static final byte KEY_NUM_DOT = 0x63;
|
||||||
|
|
||||||
|
public static final byte KEY_BACKSLASH_NON_US = 0x64;
|
||||||
|
|
||||||
public static final byte KEY_A = 0x04;
|
public static final byte KEY_A = 0x04;
|
||||||
public static final byte KEY_B = 0x05;
|
public static final byte KEY_B = 0x05;
|
||||||
public static final byte KEY_C = 0x06;
|
public static final byte KEY_C = 0x06;
|
||||||
@ -126,6 +131,133 @@ public class HIDKeycodes {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static final SparseArray<String> modifiersMap;
|
||||||
|
static
|
||||||
|
{
|
||||||
|
modifiersMap = new SparseArray<String>();
|
||||||
|
modifiersMap.put(CTRL_LEFT, "Left Ctrl");
|
||||||
|
modifiersMap.put(SHIFT_LEFT, "Left Shift");
|
||||||
|
modifiersMap.put(ALT_LEFT, "Left Alt");
|
||||||
|
modifiersMap.put(GUI_LEFT, "Left GUI");
|
||||||
|
modifiersMap.put(CTRL_RIGHT, "Right Ctrl");
|
||||||
|
modifiersMap.put(SHIFT_RIGHT, "Right Shift");
|
||||||
|
modifiersMap.put(ALT_RIGHT, "Right Alt");
|
||||||
|
modifiersMap.put(GUI_RIGHT, "Right GUI");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final SparseArray<String> keyMap;
|
||||||
|
static
|
||||||
|
{
|
||||||
|
keyMap = new SparseArray<String>();
|
||||||
|
keyMap.put(0, "None");
|
||||||
|
keyMap.put(KEY_ENTER, "Enter");
|
||||||
|
keyMap.put(KEY_ESCAPE , "Esc");
|
||||||
|
keyMap.put(KEY_BACKSPACE , "Backspace");
|
||||||
|
keyMap.put(KEY_TAB , "Tab");
|
||||||
|
keyMap.put(KEY_SPACEBAR , "Space");
|
||||||
|
|
||||||
|
keyMap.put(KEY_CAPS_LOCK , "CapsLock");
|
||||||
|
|
||||||
|
keyMap.put(KEY_1 , "1");
|
||||||
|
keyMap.put(KEY_2 , "2");
|
||||||
|
keyMap.put(KEY_3 , "3");
|
||||||
|
keyMap.put(KEY_4 , "4");
|
||||||
|
keyMap.put(KEY_5 , "5");
|
||||||
|
keyMap.put(KEY_6 , "6");
|
||||||
|
keyMap.put(KEY_7 , "7");
|
||||||
|
keyMap.put(KEY_8 , "8");
|
||||||
|
keyMap.put(KEY_9 , "9");
|
||||||
|
keyMap.put(KEY_0 , "0");
|
||||||
|
|
||||||
|
keyMap.put(KEY_F1 , "F1");
|
||||||
|
keyMap.put(KEY_F2 , "F2");
|
||||||
|
keyMap.put(KEY_F3 , "F3");
|
||||||
|
keyMap.put(KEY_F4 , "F4");
|
||||||
|
keyMap.put(KEY_F5 , "F5");
|
||||||
|
keyMap.put(KEY_F6 , "F6");
|
||||||
|
keyMap.put(KEY_F7 , "F7");
|
||||||
|
keyMap.put(KEY_F8 , "F8");
|
||||||
|
keyMap.put(KEY_F9 , "F9");
|
||||||
|
keyMap.put(KEY_F10 , "F10");
|
||||||
|
keyMap.put(KEY_F11 , "F11");
|
||||||
|
keyMap.put(KEY_F12 , "F12");
|
||||||
|
|
||||||
|
keyMap.put(KEY_PRINT_SCREEN , "Print Scrn");
|
||||||
|
keyMap.put(KEY_SCROLL_LOCK , "ScrollLock");
|
||||||
|
keyMap.put(KEY_PASUE , "Pause Break");
|
||||||
|
keyMap.put(KEY_INSERT , "Insert");
|
||||||
|
keyMap.put(KEY_HOME , "Home");
|
||||||
|
keyMap.put(KEY_PAGE_UP , "PageUp");
|
||||||
|
keyMap.put(KEY_DELETE , "Delete");
|
||||||
|
keyMap.put(KEY_END , "End");
|
||||||
|
keyMap.put(KEY_PAGE_DOWN , "PageDown");
|
||||||
|
|
||||||
|
keyMap.put(KEY_ARROW_RIGHT , "Right Arrow");
|
||||||
|
keyMap.put(KEY_ARROW_LEFT , "Left Arrow");
|
||||||
|
keyMap.put(KEY_ARROW_DOWN , "Down Arrow");
|
||||||
|
keyMap.put(KEY_ARROW_UP , "Up Arrow");
|
||||||
|
|
||||||
|
keyMap.put(KEY_NUM_LOCK , "NumLock");
|
||||||
|
keyMap.put(KEY_NUM_BACKSLASH , "Num /");
|
||||||
|
keyMap.put(KEY_NUM_STAR , "Num *");
|
||||||
|
keyMap.put(KEY_NUM_MINUS , "Num -");
|
||||||
|
keyMap.put(KEY_NUM_PLUS , "Num +");
|
||||||
|
keyMap.put(KEY_NUM_ENTER , "Num Enter");
|
||||||
|
keyMap.put(KEY_NUM_1 , "Num 1");
|
||||||
|
keyMap.put(KEY_NUM_2 , "Num 2");
|
||||||
|
keyMap.put(KEY_NUM_3 , "Num 3");
|
||||||
|
keyMap.put(KEY_NUM_4 , "Num 4");
|
||||||
|
keyMap.put(KEY_NUM_5 , "Num 5");
|
||||||
|
keyMap.put(KEY_NUM_6 , "Num 6");
|
||||||
|
keyMap.put(KEY_NUM_7 , "Num 7");
|
||||||
|
keyMap.put(KEY_NUM_8 , "Num 8");
|
||||||
|
keyMap.put(KEY_NUM_9 , "Num 9");
|
||||||
|
keyMap.put(KEY_NUM_0 , "Num 0");
|
||||||
|
keyMap.put(KEY_NUM_DOT , "Num .");
|
||||||
|
|
||||||
|
keyMap.put(KEY_A , "A");
|
||||||
|
keyMap.put(KEY_B , "B");
|
||||||
|
keyMap.put(KEY_C , "C");
|
||||||
|
keyMap.put(KEY_D , "D");
|
||||||
|
keyMap.put(KEY_E , "E");
|
||||||
|
keyMap.put(KEY_F , "F");
|
||||||
|
keyMap.put(KEY_G , "G");
|
||||||
|
keyMap.put(KEY_H , "H");
|
||||||
|
keyMap.put(KEY_I , "I");
|
||||||
|
keyMap.put(KEY_J , "J");
|
||||||
|
keyMap.put(KEY_K , "K");
|
||||||
|
keyMap.put(KEY_L , "L");
|
||||||
|
keyMap.put(KEY_M , "M");
|
||||||
|
keyMap.put(KEY_N , "N");
|
||||||
|
keyMap.put(KEY_O , "O");
|
||||||
|
keyMap.put(KEY_P , "P");
|
||||||
|
keyMap.put(KEY_Q , "Q");
|
||||||
|
keyMap.put(KEY_R , "R");
|
||||||
|
keyMap.put(KEY_S , "S");
|
||||||
|
keyMap.put(KEY_T , "T");
|
||||||
|
keyMap.put(KEY_U , "U");
|
||||||
|
keyMap.put(KEY_V , "V");
|
||||||
|
keyMap.put(KEY_W , "W");
|
||||||
|
keyMap.put(KEY_X , "X");
|
||||||
|
keyMap.put(KEY_Y , "Y");
|
||||||
|
keyMap.put(KEY_Z , "Z");
|
||||||
|
|
||||||
|
keyMap.put(KEY_MINUS , "-");
|
||||||
|
keyMap.put(KEY_EQUALS , "=");
|
||||||
|
keyMap.put(KEY_LEFT_BRACKET , "[");
|
||||||
|
keyMap.put(KEY_RIGHT_BRACKET , "]");
|
||||||
|
keyMap.put(KEY_BACKSLASH , "\\");
|
||||||
|
//keyMap.put(KEY_GRAVE , "`");
|
||||||
|
keyMap.put(KEY_SEMICOLON , ";");
|
||||||
|
keyMap.put(KEY_APOSTROPHE , "'");
|
||||||
|
keyMap.put(KEY_GRAVE , "`");
|
||||||
|
keyMap.put(KEY_COMA , ",");
|
||||||
|
keyMap.put(KEY_DOT , ".");
|
||||||
|
keyMap.put(KEY_SLASH , "/");
|
||||||
|
|
||||||
|
keyMap.put(KEY_APPLICATION , "Application");
|
||||||
|
}
|
||||||
|
|
||||||
public static final int[] ASCIItoHID = {
|
public static final int[] ASCIItoHID = {
|
||||||
0, //000
|
0, //000
|
||||||
0, //001
|
0, //001
|
||||||
@ -257,6 +389,14 @@ public class HIDKeycodes {
|
|||||||
0 //127 just in case...
|
0 //127 just in case...
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static char getChar(byte keyCode) {
|
||||||
|
for (int i = 0; i < ASCIItoHID.length; i++) {
|
||||||
|
if (ASCIItoHID[i] == keyCode) {
|
||||||
|
return (char)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public static byte getKeyCode(char c) {
|
public static byte getKeyCode(char c) {
|
||||||
return (byte)ASCIItoHID[c]; //TODO range
|
return (byte)ASCIItoHID[c]; //TODO range
|
||||||
@ -266,7 +406,31 @@ public class HIDKeycodes {
|
|||||||
return ASCIItoHID[c]; //TODO range
|
return ASCIItoHID[c]; //TODO range
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String modifiersToString(byte modifiers) {
|
||||||
|
String result = "None";
|
||||||
|
boolean first = true;
|
||||||
|
byte mod;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
mod = (byte)(CTRL_LEFT << i);
|
||||||
|
if ((modifiers & mod) != 0) {
|
||||||
|
if ( !first) {
|
||||||
|
result += ", ";
|
||||||
|
} else {
|
||||||
|
result = "";
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
result += modifiersMap.get(mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String keyToString(byte key) {
|
||||||
|
String result = keyMap.get(key);
|
||||||
|
if (result == null) {
|
||||||
|
result = "Unknown";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,71 @@
|
|||||||
package com.inputstick.api.hid;
|
package com.inputstick.api.hid;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import com.inputstick.api.ConnectionManager;
|
import com.inputstick.api.ConnectionManager;
|
||||||
|
import com.inputstick.api.HIDInfo;
|
||||||
|
import com.inputstick.api.OnEmptyBufferListener;
|
||||||
import com.inputstick.api.Packet;
|
import com.inputstick.api.Packet;
|
||||||
|
import com.inputstick.api.basic.InputStickHID;
|
||||||
|
|
||||||
public class HIDTransactionQueue {
|
public class HIDTransactionQueue {
|
||||||
|
|
||||||
public static final int KEYBOARD = 1;
|
|
||||||
public static final int MOUSE = 2;
|
|
||||||
public static final int CONSUMER = 3;
|
|
||||||
|
|
||||||
private static final int BUFFER_SIZE = 32;
|
private static final int BUFFER_SIZE = 32;
|
||||||
|
private static final int BT_DELAY = 50; //additional delay for BT overhead
|
||||||
|
|
||||||
|
private static final int MAX_PACKETS_PER_UPDATE = 10;
|
||||||
|
private static final int MAX_IMMEDIATE_PACKETS = 3;
|
||||||
|
|
||||||
private final Vector<HIDTransaction> queue;
|
private final Vector<HIDTransaction> queue;
|
||||||
private final ConnectionManager mConnectionManager;
|
private final ConnectionManager mConnectionManager;
|
||||||
private final byte cmd;
|
private final byte cmd;
|
||||||
private boolean ready;
|
private boolean ready;
|
||||||
|
|
||||||
|
private int mInterfaceType;
|
||||||
|
private boolean mustNotify;
|
||||||
|
private Vector<OnEmptyBufferListener> mBufferEmptyListeners = new Vector<OnEmptyBufferListener>();
|
||||||
|
|
||||||
|
private Timer t;
|
||||||
|
private boolean timerCancelled;
|
||||||
|
private boolean sentAhead;
|
||||||
private long lastTime;
|
private long lastTime;
|
||||||
private int lastReports;
|
private long minNextTime;
|
||||||
|
private int lastReports;
|
||||||
|
|
||||||
|
|
||||||
public HIDTransactionQueue(int type, ConnectionManager connectionManager) {
|
// >= FW 0.93
|
||||||
|
private boolean bufferInitDone;
|
||||||
|
private boolean constantUpdateMode;
|
||||||
|
private int bufferFreeSpace;
|
||||||
|
private int immediatePacketsLeft;
|
||||||
|
//private int reportsSentSinceLastUpdate;
|
||||||
|
private int packetsSentSinceLastUpdate;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public HIDTransactionQueue(int interfaceType, ConnectionManager connectionManager) {
|
||||||
|
constantUpdateMode = false;
|
||||||
|
bufferFreeSpace = BUFFER_SIZE;
|
||||||
|
|
||||||
queue = new Vector<HIDTransaction>();
|
queue = new Vector<HIDTransaction>();
|
||||||
mConnectionManager = connectionManager;
|
mConnectionManager = connectionManager;
|
||||||
ready = false;
|
ready = false;
|
||||||
switch (type) {
|
sentAhead = false;
|
||||||
case KEYBOARD:
|
minNextTime = 0;
|
||||||
|
|
||||||
|
mustNotify = false;
|
||||||
|
|
||||||
|
mInterfaceType = interfaceType;
|
||||||
|
switch (interfaceType) {
|
||||||
|
case InputStickHID.INTERFACE_KEYBOARD:
|
||||||
cmd = Packet.CMD_HID_DATA_KEYB;
|
cmd = Packet.CMD_HID_DATA_KEYB;
|
||||||
break;
|
break;
|
||||||
case MOUSE:
|
case InputStickHID.INTERFACE_MOUSE:
|
||||||
cmd = Packet.CMD_HID_DATA_MOUSE;
|
cmd = Packet.CMD_HID_DATA_MOUSE;
|
||||||
break;
|
break;
|
||||||
case CONSUMER:
|
case InputStickHID.INTERFACE_CONSUMER:
|
||||||
cmd = Packet.CMD_HID_DATA_CONSUMER;
|
cmd = Packet.CMD_HID_DATA_CONSUMER;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -41,26 +73,32 @@ public class HIDTransactionQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendNext() {
|
private int sendNext(int maxReports) {
|
||||||
HIDTransaction transaction;
|
HIDTransaction transaction;
|
||||||
byte reports = 0;
|
|
||||||
ready = false;
|
|
||||||
Packet p = new Packet(false, cmd, reports);
|
|
||||||
|
|
||||||
//assume there is at least 1 element in queue
|
//assume there is at least 1 element in queue
|
||||||
transaction = queue.firstElement();
|
transaction = queue.firstElement();
|
||||||
if (transaction.getReportsCount() > BUFFER_SIZE) {
|
if (transaction.getReportsCount() > maxReports) {
|
||||||
//transaction too big! split
|
// v0.92
|
||||||
transaction = transaction.split(BUFFER_SIZE);
|
if (maxReports < BUFFER_SIZE) {
|
||||||
|
//don't split transactions until there is no other way left!
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//transaction too big to fit single packet! split
|
||||||
|
transaction = transaction.split(BUFFER_SIZE);
|
||||||
} else {
|
} else {
|
||||||
queue.removeElementAt(0);
|
queue.removeElementAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte reports = 0;
|
||||||
|
ready = false;
|
||||||
|
Packet p = new Packet(false, cmd, reports);
|
||||||
|
|
||||||
while (transaction.hasNext()) {
|
while (transaction.hasNext()) {
|
||||||
p.addBytes(transaction.getNextReport());
|
p.addBytes(transaction.getNextReport());
|
||||||
reports++;
|
reports++;
|
||||||
}
|
}
|
||||||
//TODO add next transactions if possible
|
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
if (queue.isEmpty()) {
|
if (queue.isEmpty()) {
|
||||||
@ -68,7 +106,7 @@ public class HIDTransactionQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
transaction = queue.firstElement();
|
transaction = queue.firstElement();
|
||||||
if (reports + transaction.getReportsCount() < BUFFER_SIZE) {
|
if (reports + transaction.getReportsCount() < maxReports) {
|
||||||
queue.removeElementAt(0);
|
queue.removeElementAt(0);
|
||||||
while (transaction.hasNext()) {
|
while (transaction.hasNext()) {
|
||||||
p.addBytes(transaction.getNextReport());
|
p.addBytes(transaction.getNextReport());
|
||||||
@ -79,33 +117,183 @@ public class HIDTransactionQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//!! total number of reports must be < 32 ! (max packet limitation)
|
||||||
p.modifyByte(1, reports); //set reports count
|
p.modifyByte(1, reports); //set reports count
|
||||||
mConnectionManager.sendPacket(p);
|
mConnectionManager.sendPacket(p);
|
||||||
|
|
||||||
lastReports = reports;
|
lastReports = reports;
|
||||||
lastTime = System.currentTimeMillis();
|
lastTime = System.currentTimeMillis();
|
||||||
|
minNextTime = lastTime + (lastReports * 4) + BT_DELAY;
|
||||||
|
|
||||||
|
if (queue.isEmpty()) {
|
||||||
|
notifyOnLocalBufferEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return reports;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTransaction(HIDTransaction transaction) {
|
public void addBufferEmptyListener(OnEmptyBufferListener listener) {
|
||||||
if (queue.isEmpty()) {
|
if (listener != null) {
|
||||||
if (System.currentTimeMillis() > lastTime + (lastReports * 8 * 2/*just to be safe*/)) {
|
if ( !mBufferEmptyListeners.contains(listener)) {
|
||||||
ready = true;
|
mBufferEmptyListeners.add(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBufferEmptyListener(OnEmptyBufferListener listener) {
|
||||||
|
if (listener != null) {
|
||||||
|
mBufferEmptyListeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyOnRemoteBufferEmpty() {
|
||||||
|
for (OnEmptyBufferListener listener : mBufferEmptyListeners) {
|
||||||
|
listener.onRemoteBufferEmpty(mInterfaceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyOnLocalBufferEmpty() {
|
||||||
|
for (OnEmptyBufferListener listener : mBufferEmptyListeners) {
|
||||||
|
listener.onRemoteBufferEmpty(mInterfaceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isLocalBufferEmpty() {
|
||||||
|
return queue.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isRemoteBufferEmpty() {
|
||||||
|
if ((queue.isEmpty()) && (bufferFreeSpace == BUFFER_SIZE)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queue.isEmpty() && ( !mustNotify)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void clearBuffer() {
|
||||||
|
queue.removeAllElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void addTransaction(HIDTransaction transaction) {
|
||||||
|
if ( !bufferInitDone) {
|
||||||
|
queue.add(transaction);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (constantUpdateMode) {
|
||||||
|
queue.add(transaction);
|
||||||
|
sendToBuffer(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mustNotify = true;
|
||||||
|
//using sentAhead will slow down mouse. FW0.92 will solve the problems
|
||||||
|
if ((queue.isEmpty()) && (System.currentTimeMillis() > minNextTime) /*&& ( !sentAhead)*/) {
|
||||||
|
sentAhead = true;
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
queue.add(transaction);
|
queue.add(transaction);
|
||||||
if (ready) {
|
if (ready) {
|
||||||
sendNext();
|
sendNext(BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void timerAction() {
|
||||||
|
if ( !timerCancelled) {
|
||||||
|
if (sentAhead) {
|
||||||
|
deviceReady(null, 0); //will set sentAhead to false;
|
||||||
|
sentAhead = true; //restore value
|
||||||
|
} else {
|
||||||
|
deviceReady(null, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void deviceReady(HIDInfo hidInfo, int reportsSentToHost) {
|
||||||
|
//it is possible that in the meantime some packets has been sent to IS!!!
|
||||||
|
|
||||||
|
bufferInitDone = true;
|
||||||
|
|
||||||
|
if (hidInfo != null) {
|
||||||
|
if (hidInfo.isSentToHostInfoAvailable()) {
|
||||||
|
constantUpdateMode = true;
|
||||||
|
// >= FW 0.93
|
||||||
|
bufferFreeSpace += reportsSentToHost;
|
||||||
|
if ((bufferFreeSpace == BUFFER_SIZE) && (queue.isEmpty())) {
|
||||||
|
notifyOnRemoteBufferEmpty();
|
||||||
|
}
|
||||||
|
immediatePacketsLeft = MAX_IMMEDIATE_PACKETS;
|
||||||
|
//reportsSentSinceLastUpdate = 0;
|
||||||
|
packetsSentSinceLastUpdate = 0;
|
||||||
|
sendToBuffer(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
//System.out.println("v90 HID update");
|
||||||
|
if (now < minNextTime) {
|
||||||
|
//set timer, just in case if deviceReady won't be called again
|
||||||
|
timerCancelled = false;
|
||||||
|
t = new Timer();
|
||||||
|
t.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
timerAction();
|
||||||
|
}
|
||||||
|
}, (minNextTime - now + 1));
|
||||||
|
} else {
|
||||||
|
timerCancelled = true;
|
||||||
|
sentAhead = false;
|
||||||
|
if (!queue.isEmpty()) {
|
||||||
|
sendNext(BUFFER_SIZE);
|
||||||
|
} else {
|
||||||
|
ready = true;
|
||||||
|
//queue is empty, InputStick reported that buffer is empty, data was added since last notification
|
||||||
|
if (mustNotify) {
|
||||||
|
notifyOnRemoteBufferEmpty();
|
||||||
|
mustNotify = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void sendToBuffer(boolean justAdded) {
|
||||||
|
if ((justAdded) && (immediatePacketsLeft <= 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !InputStickHID.isReady()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queue.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bufferFreeSpace <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (packetsSentSinceLastUpdate >= MAX_PACKETS_PER_UPDATE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int reportsSent = sendNext(bufferFreeSpace);
|
||||||
|
if (reportsSent > 0) {
|
||||||
|
if (justAdded) {
|
||||||
|
immediatePacketsLeft --;
|
||||||
|
}
|
||||||
|
bufferFreeSpace -= reportsSent;
|
||||||
|
packetsSentSinceLastUpdate ++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deviceReady() {
|
|
||||||
if (!queue.isEmpty()) {
|
|
||||||
sendNext();
|
|
||||||
} else {
|
|
||||||
ready = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,49 @@ public class GermanLayout extends KeyboardLayout {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final int DEADKEYS[] = {
|
||||||
|
0x0060, 0x00b4, 0x005e
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final int DEADKEY_LUT[][] = {
|
||||||
|
{ 0x00b4 , 0x0079 , 0x00fd } ,
|
||||||
|
{ 0x00b4 , 0x0061 , 0x00e1 } ,
|
||||||
|
{ 0x00b4 , 0x0065 , 0x00e9 } ,
|
||||||
|
{ 0x00b4 , 0x0075 , 0x00fa } ,
|
||||||
|
{ 0x00b4 , 0x0069 , 0x00ed } ,
|
||||||
|
{ 0x00b4 , 0x006f , 0x00f3 } ,
|
||||||
|
{ 0x00b4 , 0x0059 , 0x00dd } ,
|
||||||
|
{ 0x00b4 , 0x0041 , 0x00c1 } ,
|
||||||
|
{ 0x00b4 , 0x0045 , 0x00c9 } ,
|
||||||
|
{ 0x00b4 , 0x0055 , 0x00da } ,
|
||||||
|
{ 0x00b4 , 0x0049 , 0x00cd } ,
|
||||||
|
{ 0x00b4 , 0x004f , 0x00d3 } ,
|
||||||
|
{ 0x00b4 , 0x0020 , 0x00b4 } ,
|
||||||
|
{ 0x0060 , 0x0061 , 0x00e0 } ,
|
||||||
|
{ 0x0060 , 0x0065 , 0x00e8 } ,
|
||||||
|
{ 0x0060 , 0x0075 , 0x00f9 } ,
|
||||||
|
{ 0x0060 , 0x0069 , 0x00ec } ,
|
||||||
|
{ 0x0060 , 0x006f , 0x00f2 } ,
|
||||||
|
{ 0x0060 , 0x0041 , 0x00c0 } ,
|
||||||
|
{ 0x0060 , 0x0045 , 0x00c8 } ,
|
||||||
|
{ 0x0060 , 0x0055 , 0x00d9 } ,
|
||||||
|
{ 0x0060 , 0x0049 , 0x00cc } ,
|
||||||
|
{ 0x0060 , 0x004f , 0x00d2 } ,
|
||||||
|
{ 0x0060 , 0x0020 , 0x0060 } ,
|
||||||
|
{ 0x005e , 0x0061 , 0x00e2 } ,
|
||||||
|
{ 0x005e , 0x0065 , 0x00ea } ,
|
||||||
|
{ 0x005e , 0x0075 , 0x00fb } ,
|
||||||
|
{ 0x005e , 0x0069 , 0x00ee } ,
|
||||||
|
{ 0x005e , 0x006f , 0x00f4 } ,
|
||||||
|
{ 0x005e , 0x0041 , 0x00c2 } ,
|
||||||
|
{ 0x005e , 0x0045 , 0x00ca } ,
|
||||||
|
{ 0x005e , 0x0055 , 0x00db } ,
|
||||||
|
{ 0x005e , 0x0049 , 0x00ce } ,
|
||||||
|
{ 0x005e , 0x004f , 0x00d4 } ,
|
||||||
|
{ 0x005e , 0x0020 , 0x005e } ,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
private static GermanLayout instance = new GermanLayout();
|
private static GermanLayout instance = new GermanLayout();
|
||||||
|
|
||||||
private GermanLayout() {
|
private GermanLayout() {
|
||||||
@ -126,9 +169,14 @@ public class GermanLayout extends KeyboardLayout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void type(String text) {
|
public void type(String text) {
|
||||||
super.type(LUT, text);
|
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, (byte)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void type(String text, byte modifiers) {
|
||||||
|
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public char getChar(int scanCode, boolean capsLock, boolean shift, boolean altGr) {
|
public char getChar(int scanCode, boolean capsLock, boolean shift, boolean altGr) {
|
||||||
return super.getChar(LUT, scanCode, capsLock, shift, altGr);
|
return super.getChar(LUT, scanCode, capsLock, shift, altGr);
|
||||||
@ -137,6 +185,16 @@ public class GermanLayout extends KeyboardLayout {
|
|||||||
@Override
|
@Override
|
||||||
public String getLocaleName() {
|
public String getLocaleName() {
|
||||||
return LOCALE_NAME;
|
return LOCALE_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[][] getDeadkeyLUT() {
|
||||||
|
return DEADKEY_LUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getDeadkeys() {
|
||||||
|
return DEADKEYS;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ public abstract class KeyboardLayout {
|
|||||||
/* 0x53 */ HIDKeycodes.KEY_DELETE,
|
/* 0x53 */ HIDKeycodes.KEY_DELETE,
|
||||||
/* 0x54 */ 0,
|
/* 0x54 */ 0,
|
||||||
/* 0x55 */ 0,
|
/* 0x55 */ 0,
|
||||||
/* 0x56 */ 0,
|
/* 0x56 */ HIDKeycodes.KEY_BACKSLASH_NON_US, //GERMAN LAYOUT!
|
||||||
/* 0x57 */ HIDKeycodes.KEY_F11,
|
/* 0x57 */ HIDKeycodes.KEY_F11,
|
||||||
/* 0x58 */ HIDKeycodes.KEY_F12,
|
/* 0x58 */ HIDKeycodes.KEY_F12,
|
||||||
/* 0x59 */ 0,
|
/* 0x59 */ 0,
|
||||||
@ -117,16 +117,19 @@ public abstract class KeyboardLayout {
|
|||||||
public static final int LAYOUT_CODE = 0;
|
public static final int LAYOUT_CODE = 0;
|
||||||
|
|
||||||
public abstract int[][] getLUT();
|
public abstract int[][] getLUT();
|
||||||
|
public abstract int[][] getDeadkeyLUT();
|
||||||
|
public abstract int[] getDeadkeys();
|
||||||
public abstract String getLocaleName();
|
public abstract String getLocaleName();
|
||||||
public abstract void type(String text);
|
public abstract void type(String text);
|
||||||
|
public abstract void type(String text, byte modifiers);
|
||||||
public abstract char getChar(int scanCode, boolean capsLock, boolean shift, boolean altGr);
|
public abstract char getChar(int scanCode, boolean capsLock, boolean shift, boolean altGr);
|
||||||
|
|
||||||
public void type(int[][] lut, String text) {
|
public void type(int[][] lut, int[][] deadkeyLUT, int[] deadkeys, String text, byte modifiers) {
|
||||||
if (InputStickHID.getState() == ConnectionManager.STATE_READY) {
|
if (InputStickHID.getState() == ConnectionManager.STATE_READY) {
|
||||||
char[] chars = text.toCharArray();
|
char[] chars = text.toCharArray();
|
||||||
HIDTransaction t;
|
HIDTransaction t;
|
||||||
for (char c : chars) {
|
for (char c : chars) {
|
||||||
t = getHIDTransaction(lut, c);
|
t = getHIDTransaction(lut, deadkeyLUT, deadkeys, c, modifiers);
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
InputStickHID.addKeyboardTransaction(t);
|
InputStickHID.addKeyboardTransaction(t);
|
||||||
}
|
}
|
||||||
@ -194,7 +197,7 @@ public abstract class KeyboardLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int getScanCode(int[][] lut, char c) {
|
public static int getScanCode(int[][] lut, char c) {
|
||||||
for (int scanCode = 0; scanCode < 80; scanCode++) {
|
for (int scanCode = 0; scanCode < 0x60; scanCode++) {
|
||||||
if (lut[scanCode][0] == -1) {
|
if (lut[scanCode][0] == -1) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
@ -233,19 +236,79 @@ public abstract class KeyboardLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static HIDTransaction getHIDTransaction(int[][] lut, char c) {
|
public static boolean isDeadkey(int[] deadkeys, char c) {
|
||||||
|
if (deadkeys != null) {
|
||||||
|
for (int key : deadkeys) {
|
||||||
|
if (key == (int)c) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int searchLUT(int[][] deadkeyLUT, char c, int returnIndex) {
|
||||||
|
if (deadkeyLUT != null) {
|
||||||
|
for (int i = 0; i < deadkeyLUT.length; i++) {
|
||||||
|
if (deadkeyLUT[i][2] == (int)c) {
|
||||||
|
return deadkeyLUT[i][returnIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int findDeadKey(int[][] deadkeyLUT, char c) {
|
||||||
|
return searchLUT(deadkeyLUT, c, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int findFollowingKey(int[][] deadkeyLUT, char c) {
|
||||||
|
return searchLUT(deadkeyLUT, c, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HIDTransaction getHIDTransaction(int[][] lut, int[][] deadkeyLUT, int[] deadkeys, char c, byte additionalModifierKeys) {
|
||||||
byte modifiers, key;
|
byte modifiers, key;
|
||||||
int scanCode;
|
int scanCode;
|
||||||
|
|
||||||
HIDTransaction t = new HIDTransaction();
|
HIDTransaction t = new HIDTransaction();
|
||||||
scanCode = getScanCode(lut, c);
|
scanCode = getScanCode(lut, c);
|
||||||
if (scanCode > 0) {
|
if (scanCode > 0) {
|
||||||
key = getKey(scanCode);
|
key = getKey(scanCode);
|
||||||
modifiers = getModifiers(lut, scanCode, c);
|
modifiers = getModifiers(lut, scanCode, c);
|
||||||
|
modifiers |= additionalModifierKeys;
|
||||||
|
|
||||||
t.addReport(new KeyboardReport(modifiers, (byte)0));
|
t.addReport(new KeyboardReport(modifiers, (byte)0));
|
||||||
t.addReport(new KeyboardReport(modifiers, key));
|
t.addReport(new KeyboardReport(modifiers, key));
|
||||||
t.addReport(new KeyboardReport());
|
t.addReport(new KeyboardReport());
|
||||||
|
|
||||||
|
//add space after deadkey!
|
||||||
|
if (isDeadkey(deadkeys, c)) {
|
||||||
|
t.addReport(new KeyboardReport((byte)0, HIDKeycodes.KEY_SPACEBAR)); //this won't work if modifiers are present!
|
||||||
|
t.addReport(new KeyboardReport());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//check if character can be obtained using deadkey:
|
||||||
|
int deadkey = findDeadKey(deadkeyLUT, c);
|
||||||
|
if (deadkey > 0) {
|
||||||
|
//yes it can
|
||||||
|
int following = findFollowingKey(deadkeyLUT, c);
|
||||||
|
|
||||||
|
scanCode = getScanCode(lut, (char)deadkey);
|
||||||
|
key = getKey(scanCode);
|
||||||
|
modifiers = getModifiers(lut, scanCode, (char)deadkey);
|
||||||
|
t.addReport(new KeyboardReport(modifiers, (byte)0));
|
||||||
|
t.addReport(new KeyboardReport(modifiers, key));
|
||||||
|
t.addReport(new KeyboardReport());
|
||||||
|
|
||||||
|
scanCode = getScanCode(lut, (char)following);
|
||||||
|
key = getKey(scanCode);
|
||||||
|
modifiers = getModifiers(lut, scanCode, (char)following);
|
||||||
|
t.addReport(new KeyboardReport(modifiers, (byte)0));
|
||||||
|
t.addReport(new KeyboardReport(modifiers, key));
|
||||||
|
t.addReport(new KeyboardReport());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
@ -260,7 +323,22 @@ public abstract class KeyboardLayout {
|
|||||||
return RussianLayout.getInstance();
|
return RussianLayout.getInstance();
|
||||||
} else if (locale.equals(GermanLayout.getInstance().getLocaleName())) {
|
} else if (locale.equals(GermanLayout.getInstance().getLocaleName())) {
|
||||||
return GermanLayout.getInstance();
|
return GermanLayout.getInstance();
|
||||||
}
|
} else if (locale.equals(SlovakLayout.getInstance().getLocaleName())) {
|
||||||
|
return SlovakLayout.getInstance();
|
||||||
|
} else if (locale.equals(PortugueseBrazilianLayout.getInstance().getLocaleName())) {
|
||||||
|
return PortugueseBrazilianLayout.getInstance();
|
||||||
|
} else if (locale.equals(DvorakLayout.getInstance().getLocaleName())) {
|
||||||
|
return DvorakLayout.getInstance();
|
||||||
|
} else if (locale.equals(NorwegianLayout.getInstance().getLocaleName())) {
|
||||||
|
return NorwegianLayout.getInstance();
|
||||||
|
} else if (locale.equals(SwedishLayout.getInstance().getLocaleName())) {
|
||||||
|
return SwedishLayout.getInstance();
|
||||||
|
} else if (locale.equals(FrenchLayout.getInstance().getLocaleName())) {
|
||||||
|
return FrenchLayout.getInstance();
|
||||||
|
} else if (locale.equals(SpanishLayout.getInstance().getLocaleName())) {
|
||||||
|
return SpanishLayout.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return UnitedStatesLayout.getInstance();
|
return UnitedStatesLayout.getInstance();
|
||||||
|
@ -111,6 +111,32 @@ public class PolishLayout extends KeyboardLayout {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final int DEADKEYS[] = {
|
||||||
|
0x007e
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final int DEADKEY_LUT[][] = {
|
||||||
|
{ 0x007e , 0x006e , 0x0144 } ,
|
||||||
|
{ 0x007e , 0x0063 , 0x0107 } ,
|
||||||
|
{ 0x007e , 0x0078 , 0x017a } ,
|
||||||
|
{ 0x007e , 0x007a , 0x017c } ,
|
||||||
|
{ 0x007e , 0x0061 , 0x0105 } ,
|
||||||
|
{ 0x007e , 0x0073 , 0x015b } ,
|
||||||
|
{ 0x007e , 0x006c , 0x0142 } ,
|
||||||
|
{ 0x007e , 0x0065 , 0x0119 } ,
|
||||||
|
{ 0x007e , 0x006f , 0x00f3 } ,
|
||||||
|
{ 0x007e , 0x004e , 0x0143 } ,
|
||||||
|
{ 0x007e , 0x0043 , 0x0106 } ,
|
||||||
|
{ 0x007e , 0x0058 , 0x0179 } ,
|
||||||
|
{ 0x007e , 0x005a , 0x017b } ,
|
||||||
|
{ 0x007e , 0x0041 , 0x0104 } ,
|
||||||
|
{ 0x007e , 0x0053 , 0x015a } ,
|
||||||
|
{ 0x007e , 0x004c , 0x0141 } ,
|
||||||
|
{ 0x007e , 0x0045 , 0x0118 } ,
|
||||||
|
{ 0x007e , 0x004f , 0x00d3 } ,
|
||||||
|
{ 0x007e , 0x0020 , 0x007e } ,
|
||||||
|
};
|
||||||
|
|
||||||
private static PolishLayout instance = new PolishLayout();
|
private static PolishLayout instance = new PolishLayout();
|
||||||
|
|
||||||
private PolishLayout() {
|
private PolishLayout() {
|
||||||
@ -127,7 +153,12 @@ public class PolishLayout extends KeyboardLayout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void type(String text) {
|
public void type(String text) {
|
||||||
super.type(LUT, text);
|
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, (byte)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void type(String text, byte modifiers) {
|
||||||
|
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -140,5 +171,14 @@ public class PolishLayout extends KeyboardLayout {
|
|||||||
return LOCALE_NAME;
|
return LOCALE_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[][] getDeadkeyLUT() {
|
||||||
|
return DEADKEY_LUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getDeadkeys() {
|
||||||
|
return DEADKEYS;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,9 @@ public class RussianLayout extends KeyboardLayout {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final int DEADKEYS[] = null;
|
||||||
|
public static final int DEADKEY_LUT[][] = null;
|
||||||
|
|
||||||
private static RussianLayout instance = new RussianLayout();
|
private static RussianLayout instance = new RussianLayout();
|
||||||
|
|
||||||
private RussianLayout() {
|
private RussianLayout() {
|
||||||
@ -128,7 +131,12 @@ public class RussianLayout extends KeyboardLayout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void type(String text) {
|
public void type(String text) {
|
||||||
super.type(LUT, text);
|
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, (byte)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void type(String text, byte modifiers) {
|
||||||
|
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -140,5 +148,15 @@ public class RussianLayout extends KeyboardLayout {
|
|||||||
public String getLocaleName() {
|
public String getLocaleName() {
|
||||||
return LOCALE_NAME;
|
return LOCALE_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[][] getDeadkeyLUT() {
|
||||||
|
return DEADKEY_LUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getDeadkeys() {
|
||||||
|
return DEADKEYS;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -98,10 +98,10 @@ public class UnitedStatesLayout extends KeyboardLayout {
|
|||||||
/* 50 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
/* 50 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||||
/* 51 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
/* 51 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||||
/* 52 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
/* 52 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||||
/* 53 */ { 0 , 0x002e , 0x002e , -1 , -1 , -1 } ,
|
/* 53 */ { 0 , 0x002e , 0x002e , -1 , -1 , -1 } ,
|
||||||
/* 54 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
/* 54 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||||
/* 55 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
/* 55 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||||
/* 56 */ { 0 , 0x005c , 0x007c , 0x001c , -1 , -1 } ,
|
/* 56 */ { 0 , 0x005c , 0x007c , 0x001c , -1 , -1 } ,
|
||||||
/* 57 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
/* 57 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||||
/* 58 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
/* 58 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||||
/* 59 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
/* 59 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
|
||||||
@ -114,6 +114,9 @@ public class UnitedStatesLayout extends KeyboardLayout {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final int DEADKEYS[] = null;
|
||||||
|
public static final int DEADKEY_LUT[][] = null;
|
||||||
|
|
||||||
private static UnitedStatesLayout instance = new UnitedStatesLayout();
|
private static UnitedStatesLayout instance = new UnitedStatesLayout();
|
||||||
|
|
||||||
private UnitedStatesLayout() {
|
private UnitedStatesLayout() {
|
||||||
@ -130,7 +133,12 @@ public class UnitedStatesLayout extends KeyboardLayout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void type(String text) {
|
public void type(String text) {
|
||||||
super.type(LUT, text);
|
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, (byte)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void type(String text, byte modifiers) {
|
||||||
|
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -141,6 +149,16 @@ public class UnitedStatesLayout extends KeyboardLayout {
|
|||||||
@Override
|
@Override
|
||||||
public String getLocaleName() {
|
public String getLocaleName() {
|
||||||
return LOCALE_NAME;
|
return LOCALE_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[][] getDeadkeyLUT() {
|
||||||
|
return DEADKEY_LUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getDeadkeys() {
|
||||||
|
return DEADKEYS;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,7 @@ package com.inputstick.init;
|
|||||||
import com.inputstick.api.Packet;
|
import com.inputstick.api.Packet;
|
||||||
|
|
||||||
|
|
||||||
public class BasicInitManager extends InitManager {
|
public class BasicInitManager extends InitManager {
|
||||||
|
|
||||||
private boolean initDone = false;
|
|
||||||
|
|
||||||
public BasicInitManager(byte[] key) {
|
public BasicInitManager(byte[] key) {
|
||||||
super(key);
|
super(key);
|
||||||
@ -14,48 +12,46 @@ public class BasicInitManager extends InitManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnected() {
|
public void onConnected() {
|
||||||
/*Packet p = new Packet(false, Packet.RAW_OLD_BOOTLOADER); //compatibility
|
/*Packet p = new Packet(false, Packet.RAW_OLD_BOOTLOADER); //compatibility with old protocol version
|
||||||
sendPacket(p);*/
|
sendPacket(p);*/
|
||||||
|
|
||||||
sendPacket(new Packet(true, Packet.CMD_RUN_FW));
|
sendPacket(new Packet(true, Packet.CMD_RUN_FW));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onData(byte[] data) {
|
public void onData(byte[] data) {
|
||||||
byte cmd = data[0];
|
byte cmd = data[0];
|
||||||
byte respCode = data[1];
|
byte respCode = data[1];
|
||||||
byte param = data[1];
|
byte param = data[1];
|
||||||
|
|
||||||
if (cmd == Packet.CMD_RUN_FW) {
|
switch (cmd) {
|
||||||
sendPacket(new Packet(true, Packet.CMD_GET_INFO));
|
case Packet.CMD_RUN_FW:
|
||||||
}
|
sendPacket(new Packet(true, Packet.CMD_FW_INFO));
|
||||||
|
break;
|
||||||
if (cmd == Packet.CMD_GET_INFO) {
|
case Packet.CMD_FW_INFO:
|
||||||
//store info
|
onFWInfo(data, true, true, new Packet(true, Packet.CMD_INIT)); //TODO next FW: params!
|
||||||
sendPacket(new Packet(true, Packet.CMD_INIT)); //TODO params!
|
break;
|
||||||
}
|
case Packet.CMD_INIT:
|
||||||
|
if (respCode == Packet.RESP_OK) {
|
||||||
if (cmd == Packet.CMD_INIT) {
|
initDone = true;
|
||||||
if (respCode == Packet.RESP_OK) {
|
sendPacket(new Packet(true, Packet.CMD_HID_STATUS_REPORT));
|
||||||
initDone = true;
|
|
||||||
sendPacket(new Packet(false, Packet.CMD_HID_STATUS_REPORT));
|
|
||||||
} else {
|
|
||||||
mListener.onInitFailure(respCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmd == Packet.CMD_HID_STATUS) {
|
|
||||||
if (initDone) {
|
|
||||||
if (param == 0x05) {
|
|
||||||
mListener.onInitReady();
|
|
||||||
} else {
|
} else {
|
||||||
mListener.onInitNotReady();
|
mListener.onInitFailure(respCode);
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
case Packet.CMD_INIT_AUTH:
|
||||||
|
onAuth(data, true, new Packet(true, Packet.CMD_INIT)); //TODO next FW: params!
|
||||||
|
break;
|
||||||
|
case Packet.CMD_HID_STATUS:
|
||||||
|
if (initDone) {
|
||||||
|
if (param == 0x05) {
|
||||||
|
mListener.onInitReady();
|
||||||
|
} else {
|
||||||
|
mListener.onInitNotReady();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,125 @@
|
|||||||
package com.inputstick.init;
|
package com.inputstick.init;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
import com.inputstick.api.InputStickError;
|
||||||
import com.inputstick.api.Packet;
|
import com.inputstick.api.Packet;
|
||||||
import com.inputstick.api.PacketManager;
|
import com.inputstick.api.PacketManager;
|
||||||
|
|
||||||
public class InitManager {
|
public class InitManager {
|
||||||
|
|
||||||
|
public static final int DEFAULT_INIT_TIMEOUT = 60000; //60s init timeout
|
||||||
|
|
||||||
protected PacketManager mPacketManager;
|
protected PacketManager mPacketManager;
|
||||||
protected InitManagerListener mListener;
|
protected InitManagerListener mListener;
|
||||||
protected byte[] mKey;
|
protected byte[] mKey;
|
||||||
|
protected DeviceInfo mInfo;
|
||||||
|
protected boolean initDone;
|
||||||
|
|
||||||
|
//private Timer t;
|
||||||
|
|
||||||
public InitManager(byte[] key) {
|
public InitManager(byte[] key) {
|
||||||
mKey = key;
|
mKey = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(InitManagerListener listener, PacketManager packetManager) {
|
|
||||||
|
public DeviceInfo getDeviceInfo() {
|
||||||
|
return mInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEncrypted() {
|
||||||
|
return mPacketManager.isEncrypted();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void init(InitManagerListener listener, PacketManager packetManager) {
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
mPacketManager = packetManager;
|
mPacketManager = packetManager;
|
||||||
|
|
||||||
|
initDone = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//WRONG THREAD!
|
||||||
|
/*public void startTimeoutCountdown(int timeout) {
|
||||||
|
t = new Timer();
|
||||||
|
t.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if ( !initDone) {
|
||||||
|
mListener.onInitFailure(InputStickError.ERROR_INIT_TIMEDOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, timeout);
|
||||||
|
}*/
|
||||||
|
|
||||||
public void onConnected() {
|
public void onConnected() {
|
||||||
mListener.onInitReady();
|
mListener.onInitReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onData(byte[] data) {
|
public void onData(byte[] data) {
|
||||||
|
//byte cmd = data[0];
|
||||||
|
//byte param = data[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacket(Packet p) {
|
public void sendPacket(Packet p) {
|
||||||
mPacketManager.sendPacket(p);
|
mPacketManager.sendPacket(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onFWInfo(byte[] data, boolean authenticate, boolean enableEncryption, Packet sendNext) {
|
||||||
|
mInfo = new DeviceInfo(data);
|
||||||
|
|
||||||
|
if (authenticate) {
|
||||||
|
if (mInfo.isPasswordProtected()) {
|
||||||
|
if (mKey != null) {
|
||||||
|
//authenticate
|
||||||
|
sendPacket(mPacketManager.encPacket(enableEncryption));
|
||||||
|
} else {
|
||||||
|
mListener.onInitFailure(InputStickError.ERROR_SECURITY_NO_KEY);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mKey != null) {
|
||||||
|
//possible scenarios: FW upgrade / password removed using other device/app / tampering!
|
||||||
|
mListener.onInitFailure(InputStickError.ERROR_SECURITY_NOT_PROTECTED);
|
||||||
|
}
|
||||||
|
sendPacket(sendNext);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendPacket(sendNext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAuth(byte[] data, boolean enableOutEncryption, Packet sendNext) {
|
||||||
|
byte respCode = data[1];
|
||||||
|
|
||||||
|
switch (respCode) {
|
||||||
|
case Packet.RESP_OK:
|
||||||
|
byte[] cmp = new byte[16];
|
||||||
|
//TODO check length!
|
||||||
|
System.arraycopy(data, 2, cmp, 0, 16);
|
||||||
|
if (mPacketManager.setEncryption(cmp, enableOutEncryption)) {
|
||||||
|
sendPacket(sendNext);
|
||||||
|
} else {
|
||||||
|
mListener.onInitFailure(InputStickError.ERROR_SECURITY_CHALLENGE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x20:
|
||||||
|
mListener.onInitFailure(InputStickError.ERROR_SECURITY_INVALID_KEY);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x21:
|
||||||
|
mListener.onInitFailure(InputStickError.ERROR_SECURITY_NOT_PROTECTED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Packet.RESP_UNKNOWN_CMD:
|
||||||
|
mListener.onInitFailure(InputStickError.ERROR_SECURITY_NOT_SUPPORTED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
mListener.onInitFailure(InputStickError.ERROR_SECURITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -47,10 +47,14 @@ package com.keepassdroid.database;
|
|||||||
|
|
||||||
// Java
|
// Java
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PushbackInputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.DigestOutputStream;
|
import java.security.DigestOutputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
@ -125,18 +129,18 @@ public class PwDatabaseV3 {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void setMasterKey(String key, String keyFileName)
|
public void setMasterKey(String key, InputStream keyfileStream)
|
||||||
throws InvalidKeyFileException, IOException {
|
throws InvalidKeyFileException, IOException {
|
||||||
assert( key != null && keyFileName != null );
|
assert( key != null && keyfileStream != null );
|
||||||
|
|
||||||
masterKey = getMasterKey(key, keyFileName);
|
masterKey = getMasterKey(key, keyfileStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected byte[] getCompositeKey(String key, String keyFileName)
|
protected byte[] getCompositeKey(String key, InputStream keyfileStream)
|
||||||
throws InvalidKeyFileException, IOException {
|
throws InvalidKeyFileException, IOException {
|
||||||
assert(key != null && keyFileName != null);
|
assert(key != null && keyfileStream != null);
|
||||||
|
|
||||||
byte[] fileKey = getFileKey(keyFileName);
|
byte[] fileKey = getFileKey(keyfileStream);
|
||||||
|
|
||||||
byte[] passwordKey = getPasswordKey(key);
|
byte[] passwordKey = getPasswordKey(key);
|
||||||
|
|
||||||
@ -151,46 +155,40 @@ public class PwDatabaseV3 {
|
|||||||
|
|
||||||
return md.digest(fileKey);
|
return md.digest(fileKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected byte[] getFileKey(String fileName)
|
protected byte[] getFileKey(InputStream keyfileStream)
|
||||||
throws InvalidKeyFileException, IOException {
|
throws InvalidKeyFileException, IOException {
|
||||||
assert(fileName != null);
|
assert(keyfileStream != null);
|
||||||
|
|
||||||
File keyfile = new File(fileName);
|
|
||||||
|
|
||||||
if ( ! keyfile.exists() ) {
|
byte[] buff = new byte[8000];
|
||||||
throw new InvalidKeyFileException();
|
|
||||||
|
int bytesRead = 0;
|
||||||
|
|
||||||
|
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
while ((bytesRead = keyfileStream.read(buff)) != -1) {
|
||||||
|
bao.write(buff, 0, bytesRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] keyFileData = bao.toByteArray();
|
||||||
|
|
||||||
|
ByteArrayInputStream bin = new ByteArrayInputStream(keyFileData);
|
||||||
|
|
||||||
byte[] key = loadXmlKeyFile(fileName);
|
|
||||||
if ( key != null ) {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileInputStream fis;
|
|
||||||
try {
|
|
||||||
fis = new FileInputStream(keyfile);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
throw new InvalidKeyFileException();
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferedInputStream bis = new BufferedInputStream(fis, 64);
|
|
||||||
|
|
||||||
long fileSize = keyfile.length();
|
if ( keyFileData.length == 32 ) {
|
||||||
if ( fileSize == 0 ) {
|
|
||||||
throw new KeyFileEmptyException();
|
|
||||||
} else if ( fileSize == 32 ) {
|
|
||||||
byte[] outputKey = new byte[32];
|
byte[] outputKey = new byte[32];
|
||||||
if ( bis.read(outputKey, 0, 32) != 32 ) {
|
if ( bin.read(outputKey, 0, 32) != 32 ) {
|
||||||
throw new IOException("Error reading key.");
|
throw new IOException("Error reading key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return outputKey;
|
return outputKey;
|
||||||
} else if ( fileSize == 64 ) {
|
} else if ( keyFileData.length == 64 ) {
|
||||||
byte[] hex = new byte[64];
|
byte[] hex = new byte[64];
|
||||||
|
|
||||||
bis.mark(64);
|
bin.mark(64);
|
||||||
if ( bis.read(hex, 0, 64) != 64 ) {
|
if ( bin.read(hex, 0, 64) != 64 ) {
|
||||||
throw new IOException("Error reading key.");
|
throw new IOException("Error reading key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +196,7 @@ public class PwDatabaseV3 {
|
|||||||
return hexStringToByteArray(new String(hex));
|
return hexStringToByteArray(new String(hex));
|
||||||
} catch (IndexOutOfBoundsException e) {
|
} catch (IndexOutOfBoundsException e) {
|
||||||
// Key is not base 64, treat it as binary data
|
// Key is not base 64, treat it as binary data
|
||||||
bis.reset();
|
bin.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +212,7 @@ public class PwDatabaseV3 {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
int bytesRead = bis.read(buffer, 0, 2048);
|
bytesRead = bin.read(buffer, 0, 2048);
|
||||||
if ( bytesRead == -1 ) break; // End of file
|
if ( bytesRead == -1 ) break; // End of file
|
||||||
|
|
||||||
md.update(buffer, 0, bytesRead);
|
md.update(buffer, 0, bytesRead);
|
||||||
@ -495,16 +493,16 @@ public class PwDatabaseV3 {
|
|||||||
return newId;
|
return newId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getMasterKey(String key, String keyFileName)
|
public byte[] getMasterKey(String key, InputStream keyfileStream)
|
||||||
throws InvalidKeyFileException, IOException {
|
throws InvalidKeyFileException, IOException {
|
||||||
assert (key != null && keyFileName != null);
|
assert (key != null && keyfileStream != null);
|
||||||
|
|
||||||
if (key.length() > 0 && keyFileName.length() > 0) {
|
if (key.length() > 0 && keyfileStream != null) {
|
||||||
return getCompositeKey(key, keyFileName);
|
return getCompositeKey(key, keyfileStream);
|
||||||
} else if (key.length() > 0) {
|
} else if (key.length() > 0) {
|
||||||
return getPasswordKey(key);
|
return getPasswordKey(key);
|
||||||
} else if (keyFileName.length() > 0) {
|
} else if (keyfileStream != null) {
|
||||||
return getFileKey(keyFileName);
|
return getFileKey(keyfileStream);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Key cannot be empty.");
|
throw new IllegalArgumentException("Key cannot be empty.");
|
||||||
}
|
}
|
||||||
@ -514,11 +512,6 @@ public class PwDatabaseV3 {
|
|||||||
public byte[] getPasswordKey(String key) throws IOException {
|
public byte[] getPasswordKey(String key) throws IOException {
|
||||||
return getPasswordKey(key, "ISO-8859-1");
|
return getPasswordKey(key, "ISO-8859-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected byte[] loadXmlKeyFile(String fileName) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public long getNumRounds() {
|
public long getNumRounds() {
|
||||||
|
@ -26,7 +26,7 @@ public class InvalidDBSignatureException extends InvalidDBException {
|
|||||||
private static final long serialVersionUID = -5358923878743513758L;
|
private static final long serialVersionUID = -5358923878743513758L;
|
||||||
|
|
||||||
public InvalidDBSignatureException() {
|
public InvalidDBSignatureException() {
|
||||||
super();
|
super("Invalid database signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,10 @@ public class InvalidKeyFileException extends InvalidDBException {
|
|||||||
private static final long serialVersionUID = 5540694419562294464L;
|
private static final long serialVersionUID = 5540694419562294464L;
|
||||||
|
|
||||||
public InvalidKeyFileException() {
|
public InvalidKeyFileException() {
|
||||||
super();
|
super("invalid key file!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidKeyFileException(String msg) {
|
||||||
|
super(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,6 @@ public class InvalidPasswordException extends InvalidDBException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public InvalidPasswordException() {
|
public InvalidPasswordException() {
|
||||||
super();
|
super("Invalid key!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,6 @@ public class KeyFileEmptyException extends InvalidKeyFileException {
|
|||||||
private static final long serialVersionUID = -1630780661204212325L;
|
private static final long serialVersionUID = -1630780661204212325L;
|
||||||
|
|
||||||
public KeyFileEmptyException() {
|
public KeyFileEmptyException() {
|
||||||
super();
|
super("key file is empty!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,13 +123,13 @@ public class ImporterV3 {
|
|||||||
* @throws InvalidAlgorithmParameterException if error decrypting main file body.
|
* @throws InvalidAlgorithmParameterException if error decrypting main file body.
|
||||||
* @throws ShortBufferException if error decrypting main file body.
|
* @throws ShortBufferException if error decrypting main file body.
|
||||||
*/
|
*/
|
||||||
public PwDatabaseV3 openDatabase( InputStream inStream, String password, String keyfile )
|
public PwDatabaseV3 openDatabase( InputStream inStream, String password, InputStream keyfileStream )
|
||||||
throws IOException, InvalidDBException
|
throws IOException, InvalidDBException
|
||||||
{
|
{
|
||||||
return openDatabase(inStream, password, keyfile, new UpdateStatus());
|
return openDatabase(inStream, password, keyfileStream, new UpdateStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PwDatabaseV3 openDatabase( InputStream inStream, String password, String keyfile, UpdateStatus status )
|
public PwDatabaseV3 openDatabase( InputStream inStream, String password, InputStream keyfileStream, UpdateStatus status )
|
||||||
throws IOException, InvalidDBException
|
throws IOException, InvalidDBException
|
||||||
{
|
{
|
||||||
PwDatabaseV3 newManager;
|
PwDatabaseV3 newManager;
|
||||||
@ -175,7 +175,7 @@ public class ImporterV3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newManager = createDB();
|
newManager = createDB();
|
||||||
newManager.setMasterKey( password, keyfile );
|
newManager.setMasterKey( password, keyfileStream );
|
||||||
|
|
||||||
// Select algorithm
|
// Select algorithm
|
||||||
if( (hdr.flags & PwDbHeaderV3.FLAG_RIJNDAEL) != 0 ) {
|
if( (hdr.flags & PwDbHeaderV3.FLAG_RIJNDAEL) != 0 ) {
|
||||||
@ -230,7 +230,7 @@ public class ImporterV3 {
|
|||||||
} catch (IllegalBlockSizeException e1) {
|
} catch (IllegalBlockSizeException e1) {
|
||||||
throw new IOException("Invalid block size");
|
throw new IOException("Invalid block size");
|
||||||
} catch (BadPaddingException e1) {
|
} catch (BadPaddingException e1) {
|
||||||
throw new InvalidPasswordException();
|
throw new InvalidPasswordException("Invalid key!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy decrypted data for testing
|
// Copy decrypted data for testing
|
||||||
@ -251,7 +251,7 @@ public class ImporterV3 {
|
|||||||
if( ! Arrays.equals(hash, hdr.contentsHash) ) {
|
if( ! Arrays.equals(hash, hdr.contentsHash) ) {
|
||||||
|
|
||||||
Log.w("KeePassDroid","Database file did not decrypt correctly. (checksum code is broken)");
|
Log.w("KeePassDroid","Database file did not decrypt correctly. (checksum code is broken)");
|
||||||
throw new InvalidPasswordException();
|
throw new InvalidPasswordException("Invalid key!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import all groups
|
// Import all groups
|
||||||
|
Binary file not shown.
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="keepass2android.plugin.inputstick"
|
package="keepass2android.plugin.inputstick"
|
||||||
android:versionCode="2"
|
android:versionCode="3"
|
||||||
android:versionName="1.1" >
|
android:versionName="1.2" >
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="14"
|
android:minSdkVersion="14"
|
||||||
|
@ -11,7 +11,10 @@ public final class R {
|
|||||||
public static final int ic_launcher = 0x7f020000;
|
public static final int ic_launcher = 0x7f020000;
|
||||||
}
|
}
|
||||||
public static final class string {
|
public static final class string {
|
||||||
|
public static final int action_settings = 0x7f060002;
|
||||||
public static final int app_name = 0x7f060000;
|
public static final int app_name = 0x7f060000;
|
||||||
|
public static final int hello_world = 0x7f060003;
|
||||||
|
public static final int title_activity_install_utility = 0x7f060001;
|
||||||
}
|
}
|
||||||
public static final class style {
|
public static final class style {
|
||||||
public static final int AppBaseTheme = 0x7f070000;
|
public static final int AppBaseTheme = 0x7f070000;
|
||||||
|
@ -49,20 +49,22 @@ public final class R {
|
|||||||
public static final int main=0x7f090000;
|
public static final int main=0x7f090000;
|
||||||
}
|
}
|
||||||
public static final class string {
|
public static final class string {
|
||||||
public static final int action_input_stick=0x7f060005;
|
public static final int action_input_stick=0x7f060007;
|
||||||
public static final int action_settings=0x7f060001;
|
public static final int action_settings=0x7f060002;
|
||||||
public static final int action_type_enter=0x7f060007;
|
public static final int action_type_enter=0x7f060009;
|
||||||
public static final int action_type_tab=0x7f060006;
|
public static final int action_type_tab=0x7f060008;
|
||||||
public static final int action_type_user_tab_pass_enter=0x7f060008;
|
public static final int action_type_user_tab_pass_enter=0x7f06000a;
|
||||||
public static final int app_name=0x7f060000;
|
public static final int app_name=0x7f060000;
|
||||||
/** Strings related to Settings
|
/** Strings related to Settings
|
||||||
*/
|
*/
|
||||||
public static final int configure_plugin=0x7f06000a;
|
public static final int configure_plugin=0x7f06000c;
|
||||||
public static final int kp2aplugin_author=0x7f060004;
|
public static final int hello_world=0x7f060003;
|
||||||
public static final int kp2aplugin_shortdesc=0x7f060003;
|
public static final int kp2aplugin_author=0x7f060006;
|
||||||
public static final int kp2aplugin_title=0x7f060002;
|
public static final int kp2aplugin_shortdesc=0x7f060005;
|
||||||
public static final int layout_title=0x7f06000b;
|
public static final int kp2aplugin_title=0x7f060004;
|
||||||
public static final int title_activity_settings=0x7f060009;
|
public static final int layout_title=0x7f06000d;
|
||||||
|
public static final int title_activity_install_utility=0x7f060001;
|
||||||
|
public static final int title_activity_settings=0x7f06000b;
|
||||||
}
|
}
|
||||||
public static final class style {
|
public static final class style {
|
||||||
/**
|
/**
|
||||||
|
@ -8,17 +8,31 @@
|
|||||||
<string name="layout_title">Host keyboard layout</string>
|
<string name="layout_title">Host keyboard layout</string>
|
||||||
|
|
||||||
<string-array name="layout_names">
|
<string-array name="layout_names">
|
||||||
<item>English (United States) - en-US</item>
|
<item>English (US)</item>
|
||||||
<item>German - de-DE</item>
|
<item>German</item>
|
||||||
<item>Polish - pl-PL</item>
|
<item>Polish</item>
|
||||||
<item>Russian - ru-RU</item>
|
<item>Russian</item>
|
||||||
|
<item>Slovak</item>
|
||||||
|
<item>Portuguese (BR)</item>
|
||||||
|
<item>Norwegian</item>
|
||||||
|
<item>Swedish</item>
|
||||||
|
<item>French</item>
|
||||||
|
<item>Spanish</item>
|
||||||
|
<item>English (Dvorak)</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="layout_values">
|
<string-array name="layout_values">
|
||||||
<item>en-US</item>
|
<item>en-US</item>
|
||||||
<item>de-DE</item>
|
<item>de-DE</item>
|
||||||
<item>pl-PL</item>
|
<item>pl-PL</item>
|
||||||
<item>ru-RU</item>
|
<item>ru-RU</item>
|
||||||
|
<item>sk-SK</item>
|
||||||
|
<item>pt-BR</item>
|
||||||
|
<item>nb-NO</item>
|
||||||
|
<item>sv-SE</item>
|
||||||
|
<item>fr-FR</item>
|
||||||
|
<item>es-ES</item>
|
||||||
|
<item>en-DV</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -34,7 +34,7 @@ public class ActionReceiver extends keepass2android.pluginsdk.PluginActionBroadc
|
|||||||
} catch (PluginAccessException e) {
|
} catch (PluginAccessException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
typeText(oe.getContext(), "");
|
//typeText(oe.getContext(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -327,9 +327,9 @@ namespace keepass2android
|
|||||||
defaultPath =>
|
defaultPath =>
|
||||||
{
|
{
|
||||||
if (defaultPath.StartsWith("sftp://"))
|
if (defaultPath.StartsWith("sftp://"))
|
||||||
Util.ShowSftpDialog(this, OnReceiveSftpData);
|
Util.ShowSftpDialog(this, OnReceiveSftpData, () => { });
|
||||||
else
|
else
|
||||||
Util.ShowFilenameDialog(this, OnCreateButton, null, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
|
Util.ShowFilenameDialog(this, OnCreateButton, null, null, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
|
||||||
Intents.RequestCodeFileBrowseForOpen);
|
Intents.RequestCodeFileBrowseForOpen);
|
||||||
}
|
}
|
||||||
), true, RequestCodeDbFilename, protocolId);
|
), true, RequestCodeDbFilename, protocolId);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
|
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
|
||||||
|
|
||||||
Keepass2Android is free software: you can redistribute it and/or modify
|
Keepass2Android is free software: you can redistribute it and/or modify
|
||||||
@ -682,7 +682,7 @@ namespace keepass2android
|
|||||||
addBinaryButton.SetCompoundDrawablesWithIntrinsicBounds( Resources.GetDrawable(Android.Resource.Drawable.IcMenuAdd) , null, null, null);
|
addBinaryButton.SetCompoundDrawablesWithIntrinsicBounds( Resources.GetDrawable(Android.Resource.Drawable.IcMenuAdd) , null, null, null);
|
||||||
addBinaryButton.Click += (sender, e) =>
|
addBinaryButton.Click += (sender, e) =>
|
||||||
{
|
{
|
||||||
Util.ShowBrowseDialog("/mnt/sdcard", this, Intents.RequestCodeFileBrowseForBinary, false);
|
Util.ShowBrowseDialog(this, Intents.RequestCodeFileBrowseForBinary, false);
|
||||||
|
|
||||||
};
|
};
|
||||||
binariesGroup.AddView(addBinaryButton,layoutParams);
|
binariesGroup.AddView(addBinaryButton,layoutParams);
|
||||||
|
@ -61,9 +61,9 @@ namespace keepass2android
|
|||||||
defaultPath =>
|
defaultPath =>
|
||||||
{
|
{
|
||||||
if (defaultPath.StartsWith("sftp://"))
|
if (defaultPath.StartsWith("sftp://"))
|
||||||
Util.ShowSftpDialog(this, OnReceiveSftpData);
|
Util.ShowSftpDialog(this, OnReceiveSftpData, () => { });
|
||||||
else
|
else
|
||||||
Util.ShowFilenameDialog(this, OnCreateButton, null, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
|
Util.ShowFilenameDialog(this, OnCreateButton, null, null, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
|
||||||
Intents.RequestCodeFileBrowseForOpen);
|
Intents.RequestCodeFileBrowseForOpen);
|
||||||
}
|
}
|
||||||
), true, RequestCodeDbFilename, protocolId);
|
), true, RequestCodeDbFilename, protocolId);
|
||||||
|
@ -14,7 +14,7 @@ namespace keepass2android
|
|||||||
[Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden , Theme="@style/NoTitleBar")]
|
[Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden , Theme="@style/NoTitleBar")]
|
||||||
public class FileStorageSelectionActivity : ListActivity
|
public class FileStorageSelectionActivity : ListActivity
|
||||||
{
|
{
|
||||||
private ActivityDesign _design;
|
private readonly ActivityDesign _design;
|
||||||
|
|
||||||
private FileStorageAdapter _fileStorageAdapter;
|
private FileStorageAdapter _fileStorageAdapter;
|
||||||
|
|
||||||
@ -42,6 +42,9 @@ namespace keepass2android
|
|||||||
//put file:// to the top
|
//put file:// to the top
|
||||||
_protocolIds.Remove("file");
|
_protocolIds.Remove("file");
|
||||||
_protocolIds.Insert(0, "file");
|
_protocolIds.Insert(0, "file");
|
||||||
|
//remove "content" (covered by androidget)
|
||||||
|
_protocolIds.Remove("content");
|
||||||
|
|
||||||
if (context.Intent.GetBooleanExtra(AllowThirdPartyAppGet, false))
|
if (context.Intent.GetBooleanExtra(AllowThirdPartyAppGet, false))
|
||||||
_protocolIds.Add("androidget");
|
_protocolIds.Add("androidget");
|
||||||
if (context.Intent.GetBooleanExtra(AllowThirdPartyAppSend, false))
|
if (context.Intent.GetBooleanExtra(AllowThirdPartyAppSend, false))
|
||||||
|
@ -24,6 +24,7 @@ using System.Xml.Serialization;
|
|||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.Database;
|
using Android.Database;
|
||||||
|
using Android.Graphics.Drawables;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
using Android.Views;
|
using Android.Views;
|
||||||
@ -86,7 +87,7 @@ namespace keepass2android
|
|||||||
private const int RequestCodePrepareDbFile = 1000;
|
private const int RequestCodePrepareDbFile = 1000;
|
||||||
private const int RequestCodePrepareOtpAuxFile = 1001;
|
private const int RequestCodePrepareOtpAuxFile = 1001;
|
||||||
private const int RequestCodeChallengeYubikey = 1002;
|
private const int RequestCodeChallengeYubikey = 1002;
|
||||||
|
private const int RequestCodeSelectKeyfile = 1003;
|
||||||
|
|
||||||
private Task<MemoryStream> _loadDbTask;
|
private Task<MemoryStream> _loadDbTask;
|
||||||
private IOConnectionInfo _ioConnection;
|
private IOConnectionInfo _ioConnection;
|
||||||
@ -136,6 +137,7 @@ namespace keepass2android
|
|||||||
|
|
||||||
private ActivityDesign _design;
|
private ActivityDesign _design;
|
||||||
private bool _performingLoad;
|
private bool _performingLoad;
|
||||||
|
|
||||||
|
|
||||||
public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer)
|
public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer)
|
||||||
: base(javaReference, transfer)
|
: base(javaReference, transfer)
|
||||||
@ -258,26 +260,20 @@ namespace keepass2android
|
|||||||
|
|
||||||
KcpKeyFile kcpKeyfile = (KcpKeyFile)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpKeyFile));
|
KcpKeyFile kcpKeyfile = (KcpKeyFile)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpKeyFile));
|
||||||
|
|
||||||
SetEditText(Resource.Id.pass_keyfile, kcpKeyfile.Path);
|
FindViewById<TextView>(Resource.Id.label_keyfilename).Text =
|
||||||
|
App.Kp2a.GetFileStorage(kcpKeyfile.Ioc).GetDisplayName(kcpKeyfile.Ioc);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
App.Kp2a.LockDatabase(false);
|
App.Kp2a.LockDatabase(false);
|
||||||
break;
|
break;
|
||||||
case Result.Ok: // Key file browse dialog OK'ed.
|
case Result.Ok:
|
||||||
if (requestCode == Intents.RequestCodeFileBrowseForKeyfile) {
|
if (requestCode == RequestCodeSelectKeyfile)
|
||||||
string filename = Util.IntentToFilename(data, this);
|
{
|
||||||
if (filename != null) {
|
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||||
if (filename.StartsWith("file://")) {
|
SetIoConnectionFromIntent(ioc, data);
|
||||||
filename = filename.Substring(7);
|
_keyFileOrProvider = IOConnectionInfo.SerializeToString(ioc);
|
||||||
}
|
UpdateKeyfileIocView();
|
||||||
|
|
||||||
filename = URLDecoder.Decode(filename);
|
|
||||||
|
|
||||||
EditText fn = (EditText) FindViewById(Resource.Id.pass_keyfile);
|
|
||||||
fn.Text = filename;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case (Result)FileStorageResults.FileUsagePrepared:
|
case (Result)FileStorageResults.FileUsagePrepared:
|
||||||
@ -354,6 +350,39 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void UpdateKeyfileIocView()
|
||||||
|
{
|
||||||
|
//store keyfile in the view so that we can show the selected keyfile again if the user switches to another key provider and back to key file
|
||||||
|
FindViewById<TextView>(Resource.Id.label_keyfilename).Tag = _keyFileOrProvider;
|
||||||
|
if (string.IsNullOrEmpty(_keyFileOrProvider))
|
||||||
|
{
|
||||||
|
FindViewById<TextView>(Resource.Id.filestorage_label).Visibility = ViewStates.Gone;
|
||||||
|
FindViewById<ImageView>(Resource.Id.filestorage_logo).Visibility = ViewStates.Gone;
|
||||||
|
FindViewById<TextView>(Resource.Id.label_keyfilename).Text = Resources.GetString(Resource.String.no_keyfile_selected);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var ioc = IOConnectionInfo.UnserializeFromString(_keyFileOrProvider);
|
||||||
|
string displayPath = App.Kp2a.GetFileStorage(ioc).GetDisplayName(ioc);
|
||||||
|
int protocolSeparatorPos = displayPath.IndexOf("://", StringComparison.Ordinal);
|
||||||
|
string protocolId = protocolSeparatorPos < 0 ?
|
||||||
|
"file" : displayPath.Substring(0, protocolSeparatorPos);
|
||||||
|
Drawable drawable = App.Kp2a.GetResourceDrawable("ic_storage_" + protocolId);
|
||||||
|
FindViewById<ImageView>(Resource.Id.filestorage_logo).SetImageDrawable(drawable);
|
||||||
|
FindViewById<ImageView>(Resource.Id.filestorage_logo).Visibility = ViewStates.Visible;
|
||||||
|
|
||||||
|
|
||||||
|
String title = App.Kp2a.GetResourceString("filestoragename_" + protocolId);
|
||||||
|
FindViewById<TextView>(Resource.Id.filestorage_label).Text = title;
|
||||||
|
FindViewById<TextView>(Resource.Id.filestorage_label).Visibility = ViewStates.Visible;
|
||||||
|
|
||||||
|
FindViewById<TextView>(Resource.Id.label_keyfilename).Text = protocolSeparatorPos < 0 ?
|
||||||
|
displayPath :
|
||||||
|
displayPath.Substring(protocolSeparatorPos + 3);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void LoadOtpFile()
|
private void LoadOtpFile()
|
||||||
{
|
{
|
||||||
new LoadingDialog<object, object, object>(this, true,
|
new LoadingDialog<object, object, object>(this, true,
|
||||||
@ -543,14 +572,11 @@ namespace keepass2android
|
|||||||
InitializeFilenameView();
|
InitializeFilenameView();
|
||||||
|
|
||||||
if (KeyProviderType == KeyProviders.KeyFile)
|
if (KeyProviderType == KeyProviders.KeyFile)
|
||||||
SetEditText(Resource.Id.pass_keyfile, _keyFileOrProvider);
|
{
|
||||||
|
UpdateKeyfileIocView();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FindViewById<EditText>(Resource.Id.pass_keyfile).TextChanged +=
|
|
||||||
(sender, args) =>
|
|
||||||
{
|
|
||||||
_keyFileOrProvider = FindViewById<EditText>(Resource.Id.pass_keyfile).Text;
|
|
||||||
UpdateOkButtonState();
|
|
||||||
};
|
|
||||||
|
|
||||||
FindViewById<EditText>(Resource.Id.password).TextChanged +=
|
FindViewById<EditText>(Resource.Id.password).TextChanged +=
|
||||||
(sender, args) =>
|
(sender, args) =>
|
||||||
@ -705,20 +731,14 @@ namespace keepass2android
|
|||||||
|
|
||||||
private void InitializeKeyfileBrowseButton()
|
private void InitializeKeyfileBrowseButton()
|
||||||
{
|
{
|
||||||
ImageButton browse = (ImageButton) FindViewById(Resource.Id.browse_button);
|
var browseButton = (Button)FindViewById(Resource.Id.btn_change_location);
|
||||||
browse.Click += (sender, evt) =>
|
browseButton.Click += (sender, evt) =>
|
||||||
{
|
{
|
||||||
string filename = null;
|
Intent intent = new Intent(this, typeof(SelectStorageLocationActivity));
|
||||||
if (!String.IsNullOrEmpty(_ioConnection.Path))
|
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, true);
|
||||||
{
|
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false);
|
||||||
File keyfile = new File(_ioConnection.Path);
|
intent.PutExtra(FileStorageSetupDefs.ExtraIsForSave, false);
|
||||||
File parent = keyfile.ParentFile;
|
StartActivityForResult(intent, RequestCodeSelectKeyfile);
|
||||||
if (parent != null)
|
|
||||||
{
|
|
||||||
filename = parent.AbsolutePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Util.ShowBrowseDialog(filename, this, Intents.RequestCodeFileBrowseForKeyfile, false);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,7 +758,7 @@ namespace keepass2android
|
|||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
//don't set to "" to prevent losing the filename. (ItemSelected is also called during recreation!)
|
//don't set to "" to prevent losing the filename. (ItemSelected is also called during recreation!)
|
||||||
_keyFileOrProvider = FindViewById<EditText>(Resource.Id.pass_keyfile).Text;
|
_keyFileOrProvider = (FindViewById(Resource.Id.label_keyfilename).Tag ?? "").ToString();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
_keyFileOrProvider = KeyProviderIdOtp;
|
_keyFileOrProvider = KeyProviderIdOtp;
|
||||||
@ -779,7 +799,7 @@ namespace keepass2android
|
|||||||
_showPassword = savedInstanceState.GetBoolean(ShowpasswordKey, false);
|
_showPassword = savedInstanceState.GetBoolean(ShowpasswordKey, false);
|
||||||
MakePasswordMaskedOrVisible();
|
MakePasswordMaskedOrVisible();
|
||||||
|
|
||||||
_keyFileOrProvider = FindViewById<EditText>(Resource.Id.pass_keyfile).Text = savedInstanceState.GetString(KeyFileOrProviderKey);
|
_keyFileOrProvider = savedInstanceState.GetString(KeyFileOrProviderKey);
|
||||||
_password = FindViewById<EditText>(Resource.Id.password).Text = savedInstanceState.GetString(PasswordKey);
|
_password = FindViewById<EditText>(Resource.Id.password).Text = savedInstanceState.GetString(PasswordKey);
|
||||||
|
|
||||||
_pendingOtps = new List<string>(savedInstanceState.GetStringArrayList(PendingOtpsKey));
|
_pendingOtps = new List<string>(savedInstanceState.GetStringArrayList(PendingOtpsKey));
|
||||||
@ -850,6 +870,11 @@ namespace keepass2android
|
|||||||
FindViewById(Resource.Id.keyfileLine).Visibility = KeyProviderType == KeyProviders.KeyFile
|
FindViewById(Resource.Id.keyfileLine).Visibility = KeyProviderType == KeyProviders.KeyFile
|
||||||
? ViewStates.Visible
|
? ViewStates.Visible
|
||||||
: ViewStates.Gone;
|
: ViewStates.Gone;
|
||||||
|
if (KeyProviderType == KeyProviders.KeyFile)
|
||||||
|
{
|
||||||
|
UpdateKeyfileIocView();
|
||||||
|
}
|
||||||
|
|
||||||
FindViewById(Resource.Id.otpView).Visibility = KeyProviderType == KeyProviders.Otp
|
FindViewById(Resource.Id.otpView).Visibility = KeyProviderType == KeyProviders.Otp
|
||||||
? ViewStates.Visible
|
? ViewStates.Visible
|
||||||
: ViewStates.Gone;
|
: ViewStates.Gone;
|
||||||
@ -876,16 +901,30 @@ namespace keepass2android
|
|||||||
//no need to check for validity of password because if this method is called, the Ok button was enabled (i.e. there was a valid password)
|
//no need to check for validity of password because if this method is called, the Ok button was enabled (i.e. there was a valid password)
|
||||||
CompositeKey compositeKey = new CompositeKey();
|
CompositeKey compositeKey = new CompositeKey();
|
||||||
compositeKey.AddUserKey(new KcpPassword(_password));
|
compositeKey.AddUserKey(new KcpPassword(_password));
|
||||||
if ((KeyProviderType == KeyProviders.KeyFile) && (_keyFileOrProvider != ""))
|
if (KeyProviderType == KeyProviders.KeyFile)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
compositeKey.AddUserKey(new KcpKeyFile(_keyFileOrProvider));
|
if (_keyFileOrProvider == "")
|
||||||
|
throw new System.IO.FileNotFoundException();
|
||||||
|
var ioc = IOConnectionInfo.UnserializeFromString(_keyFileOrProvider);
|
||||||
|
using (var stream = App.Kp2a.GetFileStorage(ioc).OpenFileForRead(ioc))
|
||||||
|
{
|
||||||
|
byte[] keyfileData = StreamToMemoryStream(stream).ToArray();
|
||||||
|
compositeKey.AddUserKey(new KcpKeyFile(keyfileData, ioc, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (System.IO.FileNotFoundException e)
|
||||||
|
{
|
||||||
|
Kp2aLog.Log(e.ToString());
|
||||||
|
Toast.MakeText(this, App.Kp2a.GetResourceString(UiStringKey.keyfile_does_not_exist), ToastLength.Long).Show();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Kp2aLog.Log(e.ToString());
|
Kp2aLog.Log(e.ToString());
|
||||||
Toast.MakeText(this, App.Kp2a.GetResourceString(UiStringKey.keyfile_does_not_exist), ToastLength.Long).Show();
|
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -895,11 +934,12 @@ namespace keepass2android
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var lOtps = GetOtpsFromUi();
|
var lOtps = GetOtpsFromUi();
|
||||||
|
Kp2aLog.Log("received " + lOtps.Count + " otps.");
|
||||||
OathHotpKeyProv.CreateOtpSecret(lOtps, _otpInfo);
|
OathHotpKeyProv.CreateOtpSecret(lOtps, _otpInfo);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
Kp2aLog.Log(e.ToString());
|
||||||
Toast.MakeText(this, GetString(Resource.String.OtpKeyError), ToastLength.Long).Show();
|
Toast.MakeText(this, GetString(Resource.String.OtpKeyError), ToastLength.Long).Show();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1031,23 +1071,29 @@ namespace keepass2android
|
|||||||
var fileStorage = App.Kp2a.GetFileStorage(_ioConnection);
|
var fileStorage = App.Kp2a.GetFileStorage(_ioConnection);
|
||||||
var stream = fileStorage.OpenFileForRead(_ioConnection);
|
var stream = fileStorage.OpenFileForRead(_ioConnection);
|
||||||
|
|
||||||
|
var memoryStream = StreamToMemoryStream(stream);
|
||||||
|
|
||||||
|
Kp2aLog.Log("Pre-loading database file completed");
|
||||||
|
|
||||||
|
return memoryStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MemoryStream StreamToMemoryStream(Stream stream)
|
||||||
|
{
|
||||||
var memoryStream = stream as MemoryStream;
|
var memoryStream = stream as MemoryStream;
|
||||||
if (memoryStream == null)
|
if (memoryStream == null)
|
||||||
{
|
{
|
||||||
// Read the file into memory
|
// Read the stream into memory
|
||||||
int capacity = 4096; // Default initial capacity, if stream can't report it.
|
int capacity = 4096; // Default initial capacity, if stream can't report it.
|
||||||
if (stream.CanSeek)
|
if (stream.CanSeek)
|
||||||
{
|
{
|
||||||
capacity = (int)stream.Length;
|
capacity = (int) stream.Length;
|
||||||
}
|
}
|
||||||
memoryStream = new MemoryStream(capacity);
|
memoryStream = new MemoryStream(capacity);
|
||||||
stream.CopyTo(memoryStream);
|
stream.CopyTo(memoryStream);
|
||||||
stream.Close();
|
stream.Close();
|
||||||
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
Kp2aLog.Log("Pre-loading database file completed");
|
|
||||||
|
|
||||||
return memoryStream;
|
return memoryStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
<!--
|
||||||
<provider android:name="group.pals.android.lib.ui.filechooser.providers.localfile.LocalFileProvider" android:authorities="keepass2android.keepass2android.android-filechooser.localfile" android:exported="false" />
|
<provider android:name="group.pals.android.lib.ui.filechooser.providers.localfile.LocalFileProvider" android:authorities="keepass2android.keepass2android.android-filechooser.localfile" android:exported="false" />
|
||||||
<provider android:name="group.pals.android.lib.ui.filechooser.providers.history.HistoryProvider" android:authorities="keepass2android.keepass2android.android-filechooser.history" android:exported="false" />
|
<provider android:name="group.pals.android.lib.ui.filechooser.providers.history.HistoryProvider" android:authorities="keepass2android.keepass2android.android-filechooser.history" android:exported="false" />
|
||||||
<activity android:name="group.pals.android.lib.ui.filechooser.FileChooserActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:screenOrientation="user" android:theme="@style/Afc.Theme.Light">
|
<activity android:name="group.pals.android.lib.ui.filechooser.FileChooserActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:screenOrientation="user" android:theme="@style/Afc.Theme.Light">
|
||||||
@ -29,7 +29,7 @@
|
|||||||
<category android:name="android.intent.category.OPENABLE" />
|
<category android:name="android.intent.category.OPENABLE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<service android:name="keepass2android.softkeyboard.KP2AKeyboard" android:permission="android.permission.BIND_INPUT_METHOD">
|
--> <service android:name="keepass2android.softkeyboard.KP2AKeyboard" android:permission="android.permission.BIND_INPUT_METHOD">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.view.InputMethod" />
|
<action android:name="android.view.InputMethod" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
9064
src/keepass2android/Resources/Resource.designer.cs
generated
9064
src/keepass2android/Resources/Resource.designer.cs
generated
File diff suppressed because it is too large
Load Diff
@ -86,25 +86,61 @@ android:layout_height="match_parent"
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:src="@drawable/ic_menu_view" />
|
android:src="@drawable/ic_menu_view" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/keyfileLine"
|
android:id="@+id/keyfileLine"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:baselineAligned="false"
|
android:baselineAligned="false"
|
||||||
android:orientation="horizontal">
|
android:orientation="vertical">
|
||||||
<EditText
|
|
||||||
android:id="@+id/pass_keyfile"
|
<TextView
|
||||||
android:layout_width="0px"
|
android:id="@+id/keyfile_heading"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:singleLine="true"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:text="@string/keyfile_heading" />
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:hint="@string/entry_keyfile" />
|
<LinearLayout
|
||||||
<ImageButton
|
android:orientation="horizontal"
|
||||||
android:id="@+id/browse_button"
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/filestorage_logo"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:src="@drawable/ic_launcher_folder_small" />
|
android:src="@drawable/ic_storage_file"
|
||||||
|
android:padding="5dp"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/filestorage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:text="Local file (TODO!)"
|
||||||
|
android:textSize="16dp" >
|
||||||
|
</TextView>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/label_keyfilename"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="[path]"
|
||||||
|
android:layout_marginLeft="18dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/btn_change_location"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/button_change_location"
|
||||||
|
style="@style/TextAppearance_SubElement"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/otpView"
|
android:id="@+id/otpView"
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
<string name="ShowGroupInEntry_title">Show group name in entry view</string>
|
<string name="ShowGroupInEntry_title">Show group name in entry view</string>
|
||||||
|
|
||||||
|
<string name="unknown_uri_scheme">Sorry! Keepass2Android cannot handle the returned URI %1$s. Please contact the developer!</string>
|
||||||
|
|
||||||
<string name="security_prefs">Security</string>
|
<string name="security_prefs">Security</string>
|
||||||
<string name="display_prefs">Display</string>
|
<string name="display_prefs">Display</string>
|
||||||
<string name="password_access_prefs">Password entry access</string>
|
<string name="password_access_prefs">Password entry access</string>
|
||||||
@ -68,6 +70,7 @@
|
|||||||
<string name="entry_expires">Expires</string>
|
<string name="entry_expires">Expires</string>
|
||||||
<string name="entry_group_name">Group Name</string>
|
<string name="entry_group_name">Group Name</string>
|
||||||
<string name="entry_keyfile">Key file (optional)</string>
|
<string name="entry_keyfile">Key file (optional)</string>
|
||||||
|
<string name="keyfile_heading">Key file</string>
|
||||||
<string name="entry_modified">Modified</string>
|
<string name="entry_modified">Modified</string>
|
||||||
<string name="entry_password">Password</string>
|
<string name="entry_password">Password</string>
|
||||||
<string name="entry_save">Save</string>
|
<string name="entry_save">Save</string>
|
||||||
@ -114,6 +117,7 @@
|
|||||||
<string name="invalid_algorithm">Invalid algorithm.</string>
|
<string name="invalid_algorithm">Invalid algorithm.</string>
|
||||||
<string name="invalid_db_sig">Database format not recognized.</string>
|
<string name="invalid_db_sig">Database format not recognized.</string>
|
||||||
<string name="keyfile_does_not_exist">Key file does not exist.</string>
|
<string name="keyfile_does_not_exist">Key file does not exist.</string>
|
||||||
|
<string name="no_keyfile_selected">No key file selected.</string>
|
||||||
<string name="keyfile_is_empty">Key file is empty.</string>
|
<string name="keyfile_is_empty">Key file is empty.</string>
|
||||||
<string name="length">Length</string>
|
<string name="length">Length</string>
|
||||||
<string name="list_size_title">Group list size</string>
|
<string name="list_size_title">Group list size</string>
|
||||||
@ -471,6 +475,17 @@
|
|||||||
|
|
||||||
<string name="killed_by_os">Sorry! Keepass2Android was killed by the Android OS! For security reasons, Keepass2Android did not persist your selected credentials on disk, so you need to re-open your database. Note: This should happen only very rarely. If it does, please drop me a message at crocoapps@gmail.com.</string>
|
<string name="killed_by_os">Sorry! Keepass2Android was killed by the Android OS! For security reasons, Keepass2Android did not persist your selected credentials on disk, so you need to re-open your database. Note: This should happen only very rarely. If it does, please drop me a message at crocoapps@gmail.com.</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="FileIsTemporarilyAvailable">The file is only temporarily available for Keepass2Android.</string>
|
||||||
|
<string name="FileIsReadOnly">The file you selected is read-only.</string>
|
||||||
|
<string name="FileIsReadOnlyOnKitkat">The file you selected is read-only for Keepass2Android due to restrictions on Android 4.4+.</string>
|
||||||
|
<string name="CopyFileRequired">To use it, you must copy it to another location.</string>
|
||||||
|
<string name="CopyFileRequiredForEditing">To edit it, you must copy the file to another location.</string>
|
||||||
|
<string name="ClickOkToSelectLocation">Click OK to select a location where the file should be copied.</string>
|
||||||
|
<string name="CancelReadOnly">Cancel, open read-only.</string>
|
||||||
|
|
||||||
|
<string name="CopyingFile">Copying file...</string>
|
||||||
|
|
||||||
<string name="ChangeLog_title">Change log</string>
|
<string name="ChangeLog_title">Change log</string>
|
||||||
|
|
||||||
<string name="PreviewWarning">Please note! This is a preview release and might come with some flaws! If you experience *anything* unexpected, please let me know (on Codeplex or by email).</string>
|
<string name="PreviewWarning">Please note! This is a preview release and might come with some flaws! If you experience *anything* unexpected, please let me know (on Codeplex or by email).</string>
|
||||||
|
238
src/keepass2android/SelectStorageLocationActivity.cs
Normal file
238
src/keepass2android/SelectStorageLocationActivity.cs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
using System;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
|
using Android.Widget;
|
||||||
|
using KeePassLib.Serialization;
|
||||||
|
using KeePassLib.Utility;
|
||||||
|
using keepass2android.Io;
|
||||||
|
using keepass2android.Utils;
|
||||||
|
|
||||||
|
namespace keepass2android
|
||||||
|
{
|
||||||
|
[Activity(Label = "")]
|
||||||
|
public class SelectStorageLocationActivity : SelectStorageLocationActivityBase, IDialogInterfaceOnDismissListener
|
||||||
|
{
|
||||||
|
private ActivityDesign _design;
|
||||||
|
|
||||||
|
private const string BundleKeySelectedIoc = "BundleKeySelectedIoc";
|
||||||
|
|
||||||
|
|
||||||
|
public const string ExtraKeyWritableRequirements = "EXTRA_KEY_WRITABLE_REQUIREMENTS";
|
||||||
|
|
||||||
|
public SelectStorageLocationActivity() : base(App.Kp2a)
|
||||||
|
{
|
||||||
|
_design = new ActivityDesign(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnCreate(Bundle bundle)
|
||||||
|
{
|
||||||
|
base.OnCreate(bundle);
|
||||||
|
|
||||||
|
_design.ApplyTheme();
|
||||||
|
|
||||||
|
|
||||||
|
Kp2aLog.Log("SelectStorageLocationActivity.OnCreate");
|
||||||
|
|
||||||
|
|
||||||
|
if (IsStorageSelectionForSave)
|
||||||
|
{
|
||||||
|
throw new Exception("save is not yet implemented. In StartSelectFile, no handler for onCreate is passed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allowThirdPartyGet = Intent.GetBooleanExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, false);
|
||||||
|
bool allowThirdPartySend = Intent.GetBooleanExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false);
|
||||||
|
|
||||||
|
bool isRecreated = false;
|
||||||
|
if (bundle == null)
|
||||||
|
State = new Bundle();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
State = (Bundle)bundle.Clone();
|
||||||
|
var selectedIocString = bundle.GetString(BundleKeySelectedIoc, null);
|
||||||
|
if (selectedIocString != null)
|
||||||
|
_selectedIoc = IOConnectionInfo.UnserializeFromString(selectedIocString);
|
||||||
|
isRecreated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo: handle orientation change while dialog is shown
|
||||||
|
|
||||||
|
if (!isRecreated)
|
||||||
|
{
|
||||||
|
StartFileStorageSelection(RequestCodeFileStorageSelectionForPrimarySelect, allowThirdPartyGet, allowThirdPartySend);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Bundle State { get; set; }
|
||||||
|
|
||||||
|
protected override void ShowToast(string text)
|
||||||
|
{
|
||||||
|
Toast.MakeText(this, text, ToastLength.Long).Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ShowInvalidSchemeMessage(string dataString)
|
||||||
|
{
|
||||||
|
Toast.MakeText(this, Resources.GetString(Resource.String.unknown_uri_scheme, new Java.Lang.Object[] { dataString }),
|
||||||
|
ToastLength.Long).Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string IntentToFilename(Intent data)
|
||||||
|
{
|
||||||
|
return Util.IntentToFilename(data, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent data)
|
||||||
|
{
|
||||||
|
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result ExitFileStorageSelectionOk
|
||||||
|
{
|
||||||
|
get { return KeePass.ExitFileStorageSelectionOk; }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void StartSelectFile( bool isForSave, int browseRequestCode, string protocolId)
|
||||||
|
{
|
||||||
|
var startManualFileSelect = new Action<string>(defaultPath =>
|
||||||
|
{
|
||||||
|
if (defaultPath.StartsWith("sftp://"))
|
||||||
|
Util.ShowSftpDialog(this, filename => OnReceivedSftpData(filename, browseRequestCode, isForSave), ReturnCancel);
|
||||||
|
else
|
||||||
|
//todo oncreate nur wenn for save?
|
||||||
|
Util.ShowFilenameDialog(this, filename => OnOpenButton(filename, browseRequestCode),
|
||||||
|
filename => OnOpenButton(filename, browseRequestCode),
|
||||||
|
ReturnCancel, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
|
||||||
|
browseRequestCode);
|
||||||
|
});
|
||||||
|
;
|
||||||
|
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this,
|
||||||
|
OnActivityResult,
|
||||||
|
startManualFileSelect
|
||||||
|
), isForSave, browseRequestCode, protocolId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ShowAndroidBrowseDialog(int requestCode, bool isForSave)
|
||||||
|
{
|
||||||
|
Util.ShowBrowseDialog(this, requestCode, isForSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool IsStorageSelectionForSave
|
||||||
|
{
|
||||||
|
get { return Intent.GetBooleanExtra(FileStorageSetupDefs.ExtraIsForSave, false); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override void OnSaveInstanceState(Bundle outState)
|
||||||
|
{
|
||||||
|
base.OnSaveInstanceState(outState);
|
||||||
|
|
||||||
|
outState.PutAll(State);
|
||||||
|
if (_selectedIoc != null)
|
||||||
|
outState.PutString(BundleKeySelectedIoc, IOConnectionInfo.SerializeToString(_selectedIoc));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnResume()
|
||||||
|
{
|
||||||
|
base.OnResume();
|
||||||
|
_design.ReapplyTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override void ReturnCancel()
|
||||||
|
{
|
||||||
|
SetResult(Result.Canceled);
|
||||||
|
Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ReturnOk(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
PasswordActivity.PutIoConnectionToIntent(ioc, intent);
|
||||||
|
SetResult(Result.Ok, intent);
|
||||||
|
Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ShowAlertDialog(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel)
|
||||||
|
{
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.SetPositiveButton(Android.Resource.String.Ok, onOk)
|
||||||
|
.SetMessage(message)
|
||||||
|
.SetCancelable(false)
|
||||||
|
.SetNegativeButton(Android.Resource.String.Cancel, onCancel)
|
||||||
|
.Create()
|
||||||
|
.Show();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override WritableRequirements RequestedWritableRequirements
|
||||||
|
{
|
||||||
|
get { return (WritableRequirements) Intent.GetIntExtra(ExtraKeyWritableRequirements, (int)WritableRequirements.ReadOnly); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected override void PerformCopy(Func<Action> copyAndReturnPostExecute)
|
||||||
|
{
|
||||||
|
|
||||||
|
new SimpleLoadingDialog(this, GetString(Resource.String.CopyingFile), false,
|
||||||
|
copyAndReturnPostExecute
|
||||||
|
).Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void StartFileStorageSelection(int requestCode, bool allowThirdPartyGet,
|
||||||
|
bool allowThirdPartySend)
|
||||||
|
{
|
||||||
|
#if !EXCLUDE_FILECHOOSER
|
||||||
|
|
||||||
|
Intent intent = new Intent(this, typeof(FileStorageSelectionActivity));
|
||||||
|
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, allowThirdPartyGet);
|
||||||
|
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, allowThirdPartySend);
|
||||||
|
|
||||||
|
StartActivityForResult(intent, requestCode);
|
||||||
|
#else
|
||||||
|
Toast.MakeText(this, "File chooser is excluded!", ToastLength.Long).Show();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void StartFileChooser(string defaultPath, int requestCode, bool forSave)
|
||||||
|
{
|
||||||
|
#if !EXCLUDE_FILECHOOSER
|
||||||
|
Kp2aLog.Log("FSA: defaultPath=" + defaultPath);
|
||||||
|
string fileProviderAuthority = FileChooserFileProvider.TheAuthority;
|
||||||
|
if (defaultPath.StartsWith("file://"))
|
||||||
|
{
|
||||||
|
fileProviderAuthority = PackageName + ".android-filechooser.localfile";
|
||||||
|
}
|
||||||
|
Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority,
|
||||||
|
defaultPath);
|
||||||
|
|
||||||
|
|
||||||
|
if (forSave)
|
||||||
|
{
|
||||||
|
i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.save_dialog", true);
|
||||||
|
var ext = UrlUtil.GetExtension(defaultPath);
|
||||||
|
if ((ext != String.Empty) && (ext.Contains("?")==false))
|
||||||
|
i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.default_file_ext", ext);
|
||||||
|
}
|
||||||
|
StartActivityForResult(i, requestCode);
|
||||||
|
|
||||||
|
#else
|
||||||
|
Toast.MakeText(this, "File chooser is excluded!", ToastLength.Long).Show();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void OnDismiss(IDialogInterface dialog)
|
||||||
|
{
|
||||||
|
// ReturnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -114,7 +114,7 @@ namespace OtpProviderClient
|
|||||||
| ((hash[offset + 2] & 0xff) << 8) //Math.
|
| ((hash[offset + 2] & 0xff) << 8) //Math.
|
||||||
| (hash[offset + 3] & 0xff); //Math.
|
| (hash[offset + 3] & 0xff); //Math.
|
||||||
|
|
||||||
int password = binary % (int)Math.Pow(10, _Length); //Math.
|
int password = binary % Convert.ToInt32(Math.Pow(10, _Length)); //Math.
|
||||||
return password.ToString(new string('0', _Length)); //Math.
|
return password.ToString(new string('0', _Length)); //Math.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,13 @@ using Object = Java.Lang.Object;
|
|||||||
|
|
||||||
namespace keepass2android.Utils
|
namespace keepass2android.Utils
|
||||||
{
|
{
|
||||||
public class LoadingDialog<TParams, TProgress, TResult> : AsyncTask<TParams, TProgress, TResult>
|
public class LoadingDialog<TParams, TProgress, TResult> : AsyncTask<TParams, TProgress, TResult>
|
||||||
{
|
{
|
||||||
private readonly Context _context;
|
private readonly Context _context;
|
||||||
private readonly string _message;
|
private readonly string _message;
|
||||||
private readonly bool _cancelable;
|
private readonly bool _cancelable;
|
||||||
readonly Func<Object[], Object> _doInBackground;
|
private readonly Func<Object[], Object> _doInBackground;
|
||||||
readonly Action<Object> _onPostExecute;
|
private readonly Action<Object> _onPostExecute;
|
||||||
|
|
||||||
private ProgressDialog mDialog;
|
private ProgressDialog mDialog;
|
||||||
/**
|
/**
|
||||||
@ -29,13 +29,14 @@ namespace keepass2android.Utils
|
|||||||
|
|
||||||
private Exception mLastException;
|
private Exception mLastException;
|
||||||
|
|
||||||
|
|
||||||
public LoadingDialog(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
public LoadingDialog(IntPtr javaReference, JniHandleOwnership transfer)
|
||||||
|
: base(javaReference, transfer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoadingDialog(Context context, string message, bool cancelable, Func<Object[], Object> doInBackground,
|
public LoadingDialog(Context context, string message, bool cancelable, Func<Object[], Object> doInBackground,
|
||||||
Action<Object> onPostExecute)
|
Action<Object> onPostExecute)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_message = message;
|
_message = message;
|
||||||
@ -58,7 +59,8 @@ namespace keepass2android.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoadingDialog(Context context, bool cancelable, Func<Object[], Object> doInBackground, Action<Object> onPostExecute)
|
public LoadingDialog(Context context, bool cancelable, Func<Object[], Object> doInBackground,
|
||||||
|
Action<Object> onPostExecute)
|
||||||
{
|
{
|
||||||
_message = context.GetString(Resource.String.loading);
|
_message = context.GetString(Resource.String.loading);
|
||||||
_context = context;
|
_context = context;
|
||||||
@ -89,32 +91,41 @@ namespace keepass2android.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
, mDelayTime);
|
, mDelayTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If you override this method, you must call {@code super.onCancelled()} at
|
* If you override this method, you must call {@code super.onCancelled()} at
|
||||||
* beginning of the method.
|
* beginning of the method.
|
||||||
*/
|
*/
|
||||||
protected override void OnCancelled() {
|
|
||||||
|
protected override void OnCancelled()
|
||||||
|
{
|
||||||
DoFinish();
|
DoFinish();
|
||||||
base.OnCancelled();
|
base.OnCancelled();
|
||||||
}// onCancelled()
|
}
|
||||||
|
|
||||||
private void DoFinish() {
|
// onCancelled()
|
||||||
|
|
||||||
|
private void DoFinish()
|
||||||
|
{
|
||||||
mFinished = true;
|
mFinished = true;
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* Sometime the activity has been finished before we dismiss this
|
* Sometime the activity has been finished before we dismiss this
|
||||||
* dialog, it will raise error.
|
* dialog, it will raise error.
|
||||||
*/
|
*/
|
||||||
mDialog.Dismiss();
|
mDialog.Dismiss();
|
||||||
} catch (Exception e)
|
}
|
||||||
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Kp2aLog.Log(e.ToString());
|
Kp2aLog.Log(e.ToString());
|
||||||
}
|
}
|
||||||
}// doFinish()
|
}
|
||||||
|
|
||||||
|
// doFinish()
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,18 +135,26 @@ namespace keepass2android.Utils
|
|||||||
* @param t
|
* @param t
|
||||||
* {@link Throwable}
|
* {@link Throwable}
|
||||||
*/
|
*/
|
||||||
protected void SetLastException(Exception e) {
|
|
||||||
|
protected void SetLastException(Exception e)
|
||||||
|
{
|
||||||
mLastException = e;
|
mLastException = e;
|
||||||
}// setLastException()
|
}
|
||||||
|
|
||||||
|
// setLastException()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets last exception.
|
* Gets last exception.
|
||||||
*
|
*
|
||||||
* @return {@link Throwable}
|
* @return {@link Throwable}
|
||||||
*/
|
*/
|
||||||
protected Exception GetLastException() {
|
|
||||||
|
protected Exception GetLastException()
|
||||||
|
{
|
||||||
return mLastException;
|
return mLastException;
|
||||||
}// getLastException()
|
}
|
||||||
|
|
||||||
|
// getLastException()
|
||||||
|
|
||||||
|
|
||||||
protected override Object DoInBackground(params Object[] @params)
|
protected override Object DoInBackground(params Object[] @params)
|
||||||
@ -151,14 +170,47 @@ namespace keepass2android.Utils
|
|||||||
protected override void OnPostExecute(Object result)
|
protected override void OnPostExecute(Object result)
|
||||||
{
|
{
|
||||||
DoFinish();
|
DoFinish();
|
||||||
|
|
||||||
if (_onPostExecute != null)
|
if (_onPostExecute != null)
|
||||||
_onPostExecute(result);
|
_onPostExecute(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SimpleLoadingDialog : LoadingDialog<object, object, object>
|
||||||
|
{
|
||||||
|
private class BackgroundResult : Object
|
||||||
|
{
|
||||||
|
private readonly Action _onPostExec;
|
||||||
|
|
||||||
|
public BackgroundResult(Action onPostExec)
|
||||||
|
{
|
||||||
|
_onPostExec = onPostExec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action OnPostExec
|
||||||
|
{
|
||||||
|
get { return _onPostExec; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleLoadingDialog(IntPtr javaReference, JniHandleOwnership transfer)
|
||||||
|
: base(javaReference, transfer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleLoadingDialog(Context ctx, string message, bool cancelable, Func<Action> doInBackgroundReturnOnPostExec)
|
||||||
|
: base(ctx, message, cancelable, input =>
|
||||||
|
{ return new BackgroundResult(doInBackgroundReturnOnPostExec()); }
|
||||||
|
, res => { ((BackgroundResult) res).OnPostExec(); })
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -141,7 +141,7 @@ namespace keepass2android
|
|||||||
return list.Count > 0;
|
return list.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ShowBrowseDialog(string filename, Activity act, int requestCodeBrowse, bool forSaving)
|
public static void ShowBrowseDialog(Activity act, int requestCodeBrowse, bool forSaving)
|
||||||
{
|
{
|
||||||
if ((!forSaving) && (IsIntentAvailable(act, Intent.ActionGetContent, "*/*", new List<string> { Intent.CategoryOpenable})))
|
if ((!forSaving) && (IsIntentAvailable(act, Intent.ActionGetContent, "*/*", new List<string> { Intent.CategoryOpenable})))
|
||||||
{
|
{
|
||||||
@ -223,7 +223,7 @@ namespace keepass2android
|
|||||||
|
|
||||||
public delegate bool FileSelectedHandler(string filename);
|
public delegate bool FileSelectedHandler(string filename);
|
||||||
|
|
||||||
public static void ShowSftpDialog(Activity activity, FileSelectedHandler onStartBrowse)
|
public static void ShowSftpDialog(Activity activity, FileSelectedHandler onStartBrowse, Action onCancel)
|
||||||
{
|
{
|
||||||
#if !EXCLUDE_JAVAFILESTORAGE
|
#if !EXCLUDE_JAVAFILESTORAGE
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
@ -244,7 +244,9 @@ namespace keepass2android
|
|||||||
password);
|
password);
|
||||||
onStartBrowse(sftpPath);
|
onStartBrowse(sftpPath);
|
||||||
});
|
});
|
||||||
builder.SetNegativeButton(Android.Resource.String.Cancel, (sender, args) => {});
|
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>( (sender, e) => onCancel());
|
||||||
|
|
||||||
|
builder.SetNegativeButton(Android.Resource.String.Cancel, evtH);
|
||||||
builder.SetTitle(activity.GetString(Resource.String.enter_sftp_login_title));
|
builder.SetTitle(activity.GetString(Resource.String.enter_sftp_login_title));
|
||||||
Dialog dialog = builder.Create();
|
Dialog dialog = builder.Create();
|
||||||
|
|
||||||
@ -252,16 +254,50 @@ namespace keepass2android
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ShowFilenameDialog(Activity activity, FileSelectedHandler onOpen, FileSelectedHandler onCreate, bool showBrowseButton,
|
class DismissListener: Java.Lang.Object, IDialogInterfaceOnDismissListener
|
||||||
string defaultFilename, string detailsText, int requestCodeBrowse)
|
{
|
||||||
|
private readonly Action _onDismiss;
|
||||||
|
|
||||||
|
public DismissListener(Action onDismiss)
|
||||||
|
{
|
||||||
|
_onDismiss = onDismiss;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDismiss(IDialogInterface dialog)
|
||||||
|
{
|
||||||
|
_onDismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CancelListener: Java.Lang.Object, IDialogInterfaceOnCancelListener
|
||||||
|
{
|
||||||
|
private readonly Action _onCancel;
|
||||||
|
|
||||||
|
public CancelListener(Action onCancel)
|
||||||
|
{
|
||||||
|
_onCancel = onCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCancel(IDialogInterface dialog)
|
||||||
|
{
|
||||||
|
_onCancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowFilenameDialog(Activity activity, FileSelectedHandler onOpen, FileSelectedHandler onCreate, Action onCancel, bool showBrowseButton, string defaultFilename, string detailsText, int requestCodeBrowse)
|
||||||
{
|
{
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
builder.SetView(activity.LayoutInflater.Inflate(Resource.Layout.file_selection_filename, null));
|
builder.SetView(activity.LayoutInflater.Inflate(Resource.Layout.file_selection_filename, null));
|
||||||
|
|
||||||
|
if (onCancel != null)
|
||||||
|
builder.SetOnCancelListener(new CancelListener(onCancel));
|
||||||
Dialog dialog = builder.Create();
|
Dialog dialog = builder.Create();
|
||||||
dialog.Show();
|
dialog.Show();
|
||||||
|
|
||||||
Button openButton = (Button) dialog.FindViewById(Resource.Id.open);
|
Button openButton = (Button) dialog.FindViewById(Resource.Id.open);
|
||||||
Button createButton = (Button) dialog.FindViewById(Resource.Id.create);
|
Button createButton = (Button) dialog.FindViewById(Resource.Id.create);
|
||||||
|
|
||||||
TextView enterFilenameDetails = (TextView) dialog.FindViewById(Resource.Id.label_open_by_filename_details);
|
TextView enterFilenameDetails = (TextView) dialog.FindViewById(Resource.Id.label_open_by_filename_details);
|
||||||
openButton.Visibility = onOpen != null ? ViewStates.Visible : ViewStates.Gone;
|
openButton.Visibility = onOpen != null ? ViewStates.Visible : ViewStates.Gone;
|
||||||
createButton.Visibility = onCreate != null? ViewStates.Visible : ViewStates.Gone;
|
createButton.Visibility = onCreate != null? ViewStates.Visible : ViewStates.Gone;
|
||||||
@ -288,9 +324,19 @@ namespace keepass2android
|
|||||||
if (onCreate(fileName))
|
if (onCreate(fileName))
|
||||||
dialog.Dismiss();
|
dialog.Dismiss();
|
||||||
};
|
};
|
||||||
|
|
||||||
Button cancelButton = (Button) dialog.FindViewById(Resource.Id.fnv_cancel);
|
Button cancelButton = (Button) dialog.FindViewById(Resource.Id.fnv_cancel);
|
||||||
cancelButton.Click += (sender, e) => dialog.Dismiss();
|
cancelButton.Click += delegate
|
||||||
|
{
|
||||||
|
dialog.Dismiss();
|
||||||
|
if (onCancel != null)
|
||||||
|
onCancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ImageButton browseButton = (ImageButton) dialog.FindViewById(Resource.Id.browse_button);
|
ImageButton browseButton = (ImageButton) dialog.FindViewById(Resource.Id.browse_button);
|
||||||
if (!showBrowseButton)
|
if (!showBrowseButton)
|
||||||
@ -301,7 +347,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
string filename = ((EditText) dialog.FindViewById(Resource.Id.file_filename)).Text;
|
string filename = ((EditText) dialog.FindViewById(Resource.Id.file_filename)).Text;
|
||||||
|
|
||||||
Util.ShowBrowseDialog(filename, activity, requestCodeBrowse, onCreate != null);
|
Util.ShowBrowseDialog(activity, requestCodeBrowse, onCreate != null);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -392,6 +392,10 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
|
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
|
||||||
|
{
|
||||||
|
return GetFileStorage(iocInfo, true);
|
||||||
|
}
|
||||||
|
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache)
|
||||||
{
|
{
|
||||||
if (iocInfo.IsLocalFile())
|
if (iocInfo.IsLocalFile())
|
||||||
return new BuiltInFileStorage(this);
|
return new BuiltInFileStorage(this);
|
||||||
@ -399,9 +403,8 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo);
|
IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo);
|
||||||
|
|
||||||
if (DatabaseCacheEnabled)
|
if (DatabaseCacheEnabled && allowCache)
|
||||||
{
|
{
|
||||||
//TODO
|
|
||||||
return new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this);
|
return new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -443,7 +446,8 @@ namespace keepass2android
|
|||||||
new SftpFileStorage(this),
|
new SftpFileStorage(this),
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
new BuiltInFileStorage(this)
|
new BuiltInFileStorage(this),
|
||||||
|
new AndroidContentStorage(Application.Context)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return _fileStorages;
|
return _fileStorages;
|
||||||
|
@ -69,6 +69,7 @@ namespace keepass2android
|
|||||||
view.FileSelectButtons _fileSelectButtons;
|
view.FileSelectButtons _fileSelectButtons;
|
||||||
|
|
||||||
internal AppTask AppTask;
|
internal AppTask AppTask;
|
||||||
|
private const int RequestCodeSelectIoc = 456;
|
||||||
|
|
||||||
public const string NoForwardToPasswordActivity = "NoForwardToPasswordActivity";
|
public const string NoForwardToPasswordActivity = "NoForwardToPasswordActivity";
|
||||||
|
|
||||||
@ -129,9 +130,12 @@ namespace keepass2android
|
|||||||
|
|
||||||
EventHandler openFileButtonClick = (sender, e) =>
|
EventHandler openFileButtonClick = (sender, e) =>
|
||||||
{
|
{
|
||||||
Intent intent = new Intent(this, typeof(FileStorageSelectionActivity));
|
Intent intent = new Intent(this, typeof(SelectStorageLocationActivity));
|
||||||
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, true);
|
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, true);
|
||||||
StartActivityForResult(intent, 0);
|
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false);
|
||||||
|
intent.PutExtra(SelectStorageLocationActivity.ExtraKeyWritableRequirements, (int) SelectStorageLocationActivity.WritableRequirements.WriteDesired);
|
||||||
|
intent.PutExtra(FileStorageSetupDefs.ExtraIsForSave, false);
|
||||||
|
StartActivityForResult(intent, RequestCodeSelectIoc);
|
||||||
|
|
||||||
};
|
};
|
||||||
openFileButton.Click += openFileButtonClick;
|
openFileButton.Click += openFileButtonClick;
|
||||||
@ -294,19 +298,7 @@ namespace keepass2android
|
|||||||
App.Kp2a.GetFileStorage(ioc)
|
App.Kp2a.GetFileStorage(ioc)
|
||||||
.PrepareFileUsage(new FileStorageSetupInitiatorActivity(this, OnActivityResult, null), ioc, 0, false);
|
.PrepareFileUsage(new FileStorageSetupInitiatorActivity(this, OnActivityResult, null), ioc, 0, false);
|
||||||
}
|
}
|
||||||
private bool OnOpenButton(String fileName)
|
|
||||||
{
|
|
||||||
|
|
||||||
IOConnectionInfo ioc = new IOConnectionInfo
|
|
||||||
{
|
|
||||||
Path = fileName
|
|
||||||
};
|
|
||||||
|
|
||||||
LaunchPasswordActivityForIoc(ioc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||||
{
|
{
|
||||||
@ -326,109 +318,23 @@ namespace keepass2android
|
|||||||
|
|
||||||
FillData();
|
FillData();
|
||||||
|
|
||||||
if (resultCode == KeePass.ExitFileStorageSelectionOk)
|
|
||||||
{
|
|
||||||
|
|
||||||
string protocolId = data.GetStringExtra("protocolId");
|
if (resultCode == (Result)FileStorageResults.FileUsagePrepared)
|
||||||
|
|
||||||
if (protocolId == "androidget")
|
|
||||||
{
|
|
||||||
string defaultFilename = Environment.ExternalStorageDirectory +
|
|
||||||
GetString(Resource.String.default_file_path);
|
|
||||||
Util.ShowBrowseDialog(defaultFilename, this, Intents.RequestCodeFileBrowseForOpen, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this,
|
|
||||||
OnActivityResult,
|
|
||||||
defaultPath =>
|
|
||||||
{
|
|
||||||
if (defaultPath.StartsWith("sftp://"))
|
|
||||||
Util.ShowSftpDialog(this, OnReceivedSftpData);
|
|
||||||
else
|
|
||||||
Util.ShowFilenameDialog(this, OnOpenButton, null, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
|
|
||||||
Intents.RequestCodeFileBrowseForOpen);
|
|
||||||
}
|
|
||||||
), false, 0, protocolId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (requestCode == Intents.RequestCodeFileBrowseForCreate
|
|
||||||
|| requestCode == Intents.RequestCodeFileBrowseForOpen)
|
|
||||||
&& resultCode == Result.Ok) {
|
|
||||||
string filename = Util.IntentToFilename(data, this);
|
|
||||||
if (filename != null) {
|
|
||||||
if (filename.StartsWith("file://")) {
|
|
||||||
filename = filename.Substring(7);
|
|
||||||
filename = Java.Net.URLDecoder.Decode(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestCode == Intents.RequestCodeFileBrowseForOpen)
|
|
||||||
{
|
|
||||||
IOConnectionInfo ioc = new IOConnectionInfo
|
|
||||||
{
|
|
||||||
Path = filename
|
|
||||||
};
|
|
||||||
|
|
||||||
LaunchPasswordActivityForIoc(ioc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resultCode == (Result) FileStorageResults.FileUsagePrepared)
|
|
||||||
{
|
{
|
||||||
IOConnectionInfo ioc = new IOConnectionInfo();
|
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||||
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
|
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
|
||||||
LaunchPasswordActivityForIoc(ioc);
|
LaunchPasswordActivityForIoc(ioc);
|
||||||
}
|
}
|
||||||
if (resultCode == (Result)FileStorageResults.FileChooserPrepared)
|
|
||||||
|
if ((resultCode == Result.Ok) && (requestCode == RequestCodeSelectIoc))
|
||||||
{
|
{
|
||||||
IOConnectionInfo ioc = new IOConnectionInfo();
|
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||||
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
|
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
|
||||||
#if !EXCLUDE_FILECHOOSER
|
LaunchPasswordActivityForIoc(ioc);
|
||||||
StartFileChooser(ioc.Path);
|
|
||||||
#else
|
|
||||||
LaunchPasswordActivityForIoc(new IOConnectionInfo { Path = "/mnt/sdcard/keepass/yubi.kdbx"});
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if ((resultCode == Result.Canceled) && (data != null) && (data.HasExtra("EXTRA_ERROR_MESSAGE")))
|
|
||||||
{
|
|
||||||
Toast.MakeText(this, data.GetStringExtra("EXTRA_ERROR_MESSAGE"), ToastLength.Long).Show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool OnReceivedSftpData(string filename)
|
|
||||||
{
|
|
||||||
IOConnectionInfo ioc = new IOConnectionInfo { Path = filename };
|
|
||||||
#if !EXCLUDE_FILECHOOSER
|
|
||||||
StartFileChooser(ioc.Path);
|
|
||||||
#else
|
|
||||||
LaunchPasswordActivityForIoc(ioc);
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !EXCLUDE_FILECHOOSER
|
|
||||||
private void StartFileChooser(string defaultPath)
|
|
||||||
{
|
|
||||||
Kp2aLog.Log("FSA: defaultPath="+defaultPath);
|
|
||||||
string fileProviderAuthority = FileChooserFileProvider.TheAuthority;
|
|
||||||
if (defaultPath.StartsWith("file://"))
|
|
||||||
{
|
|
||||||
fileProviderAuthority = PackageName+".android-filechooser.localfile";
|
|
||||||
}
|
|
||||||
Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority,
|
|
||||||
defaultPath);
|
|
||||||
|
|
||||||
StartActivityForResult(i, Intents.RequestCodeFileBrowseForOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
protected override void OnResume()
|
protected override void OnResume()
|
||||||
{
|
{
|
||||||
base.OnResume();
|
base.OnResume();
|
||||||
|
@ -58,6 +58,12 @@ namespace keepass2android.fileselect
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnRestart()
|
||||||
|
{
|
||||||
|
base.OnRestart();
|
||||||
|
_isRecreated = true;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnStart()
|
protected override void OnStart()
|
||||||
{
|
{
|
||||||
base.OnStart();
|
base.OnStart();
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>10.0.0</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{A6CF8A86-37C1-4197-80FE-519DE2C842F5}</ProjectGuid>
|
<ProjectGuid>{A6CF8A86-37C1-4197-80FE-519DE2C842F5}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -17,12 +15,9 @@
|
|||||||
<AssemblyName>keepass2android</AssemblyName>
|
<AssemblyName>keepass2android</AssemblyName>
|
||||||
<newfilesearch>OnLoad</newfilesearch>
|
<newfilesearch>OnLoad</newfilesearch>
|
||||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||||
<TargetFrameworkVersion>v4.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.4</TargetFrameworkVersion>
|
||||||
<AndroidStoreUncompressedFileExtensions />
|
|
||||||
<MandroidI18n />
|
<MandroidI18n />
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||||
<JavaOptions />
|
|
||||||
<AndroidUseLatestPlatformSdk />
|
|
||||||
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
@ -30,7 +25,7 @@
|
|||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>bin\Debug</OutputPath>
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;INCLUDE_KEYBOARD;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE;EXCLUDE_KEYTRANSFORM</DefineConstants>
|
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_FILECHOOSER;_EXCLUDE_JAVAFILESTORAGE;INCLUDE_KEYTRANSFORM</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<ConsolePause>False</ConsolePause>
|
<ConsolePause>False</ConsolePause>
|
||||||
@ -40,7 +35,8 @@
|
|||||||
<Command type="BeforeBuild" command="UseManifestNet.bat" />
|
<Command type="BeforeBuild" command="UseManifestNet.bat" />
|
||||||
</CustomCommands>
|
</CustomCommands>
|
||||||
</CustomCommands>
|
</CustomCommands>
|
||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
|
||||||
|
<AndroidSupportedAbis>armeabi;armeabi-v7a;x86</AndroidSupportedAbis>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
@ -50,7 +46,6 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||||
<ConsolePause>False</ConsolePause>
|
<ConsolePause>False</ConsolePause>
|
||||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
|
||||||
<AndroidSupportedAbis>armeabi,armeabi-v7a</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi,armeabi-v7a</AndroidSupportedAbis>
|
||||||
<CustomCommands>
|
<CustomCommands>
|
||||||
<CustomCommands>
|
<CustomCommands>
|
||||||
@ -61,7 +56,10 @@
|
|||||||
<DefineConstants>RELEASE</DefineConstants>
|
<DefineConstants>RELEASE</DefineConstants>
|
||||||
<AndroidLinkSkip>System.Core%3b</AndroidLinkSkip>
|
<AndroidLinkSkip>System.Core%3b</AndroidLinkSkip>
|
||||||
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
|
||||||
<BundleAssemblies>False</BundleAssemblies>
|
<AndroidStoreUncompressedFileExtensions>
|
||||||
|
</AndroidStoreUncompressedFileExtensions>
|
||||||
|
<JavaOptions>
|
||||||
|
</JavaOptions>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU' ">
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
@ -69,7 +67,6 @@
|
|||||||
<OutputPath>bin\ReleaseNoNet</OutputPath>
|
<OutputPath>bin\ReleaseNoNet</OutputPath>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
|
||||||
<DefineConstants>NoNet</DefineConstants>
|
<DefineConstants>NoNet</DefineConstants>
|
||||||
<CustomCommands>
|
<CustomCommands>
|
||||||
<CustomCommands>
|
<CustomCommands>
|
||||||
@ -78,15 +75,27 @@
|
|||||||
</CustomCommands>
|
</CustomCommands>
|
||||||
<AndroidSupportedAbis>armeabi,armeabi-v7a</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi,armeabi-v7a</AndroidSupportedAbis>
|
||||||
<DeployExternal>True</DeployExternal>
|
<DeployExternal>True</DeployExternal>
|
||||||
|
<AndroidStoreUncompressedFileExtensions>
|
||||||
|
</AndroidStoreUncompressedFileExtensions>
|
||||||
|
<JavaOptions>
|
||||||
|
</JavaOptions>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
<Reference Include="Mono.Android.Support.v4" />
|
<Reference Include="GooglePlayServicesLib">
|
||||||
<Reference Include="GooglePlayServicesFroyoLib">
|
<HintPath>..\Components\googleplayservices-19.0.0\lib\android\GooglePlayServicesLib.dll</HintPath>
|
||||||
<HintPath>..\Components\googleplayservicesfroyo-9.0\lib\android\GooglePlayServicesFroyoLib.dll</HintPath>
|
</Reference>
|
||||||
|
<Reference Include="Xamarin.Android.Support.v4">
|
||||||
|
<HintPath>..\Components\googleplayservices-19.0.0\lib\android\Xamarin.Android.Support.v4.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xamarin.Android.Support.v7.AppCompat">
|
||||||
|
<HintPath>..\Components\googleplayservices-19.0.0\lib\android\Xamarin.Android.Support.v7.AppCompat.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xamarin.Android.Support.v7.MediaRouter">
|
||||||
|
<HintPath>..\Components\googleplayservices-19.0.0\lib\android\Xamarin.Android.Support.v7.MediaRouter.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -138,6 +147,7 @@
|
|||||||
<Compile Include="fileselect\FileSelectActivity.cs" />
|
<Compile Include="fileselect\FileSelectActivity.cs" />
|
||||||
<Compile Include="fileselect\FileDbHelper.cs" />
|
<Compile Include="fileselect\FileDbHelper.cs" />
|
||||||
<Compile Include="search\SearchProvider.cs" />
|
<Compile Include="search\SearchProvider.cs" />
|
||||||
|
<Compile Include="SelectStorageLocationActivity.cs" />
|
||||||
<Compile Include="services\OngoingNotificationsService.cs" />
|
<Compile Include="services\OngoingNotificationsService.cs" />
|
||||||
<Compile Include="settings\DatabaseSettingsActivity.cs" />
|
<Compile Include="settings\DatabaseSettingsActivity.cs" />
|
||||||
<Compile Include="intents\Intents.cs" />
|
<Compile Include="intents\Intents.cs" />
|
||||||
@ -663,12 +673,8 @@
|
|||||||
<Folder Include="SupportLib\" />
|
<Folder Include="SupportLib\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AndroidFileChooserBinding\AndroidFileChooserBinding.csproj">
|
|
||||||
<Project>{3c0f7fe5-639f-4422-a087-8b26cf862d1b}</Project>
|
|
||||||
<Name>AndroidFileChooserBinding</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
|
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
|
||||||
<Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
|
<Project>{48574278-4779-4B3A-A9E4-9CF1BC285D0B}</Project>
|
||||||
<Name>JavaFileStorageBindings</Name>
|
<Name>JavaFileStorageBindings</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj">
|
<ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj">
|
||||||
@ -680,21 +686,25 @@
|
|||||||
<Name>Kp2aBusinessLogic</Name>
|
<Name>Kp2aBusinessLogic</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
|
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
|
||||||
<Project>{70d3844a-d9fa-4a64-b205-a84c6a822196}</Project>
|
<Project>{70D3844A-D9FA-4A64-B205-A84C6A822196}</Project>
|
||||||
<Name>KP2AKdbLibraryBinding</Name>
|
<Name>KP2AKdbLibraryBinding</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\Kp2aKeyboardBinding\Kp2aKeyboardBinding.csproj">
|
<ProjectReference Include="..\Kp2aKeyboardBinding\Kp2aKeyboardBinding.csproj">
|
||||||
<Project>{a8779d4d-7c49-4c2f-82bd-2cdc448391da}</Project>
|
<Project>{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}</Project>
|
||||||
<Name>Kp2aKeyboardBinding</Name>
|
<Name>Kp2aKeyboardBinding</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj">
|
<ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj">
|
||||||
<Project>{3da3911e-36de-465e-8f15-f1991b6437e5}</Project>
|
<Project>{3DA3911E-36DE-465E-8F15-F1991B6437E5}</Project>
|
||||||
<Name>PluginSdkBinding</Name>
|
<Name>PluginSdkBinding</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj">
|
<ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj">
|
||||||
<Project>{5cf675a5-9bee-4720-bed9-d5bf14a2ebf9}</Project>
|
<Project>{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}</Project>
|
||||||
<Name>TwofishCipher</Name>
|
<Name>TwofishCipher</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\AndroidFileChooserBinding\AndroidFileChooserBinding.csproj">
|
||||||
|
<Project>{3C0F7FE5-639F-4422-A087-8B26CF862D1B}</Project>
|
||||||
|
<Name>AndroidFileChooserBinding</Name>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ProjectExtensions>
|
<ProjectExtensions>
|
||||||
<MonoDevelop>
|
<MonoDevelop>
|
||||||
@ -832,12 +842,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\layout\text_with_help.xml" />
|
<AndroidResource Include="Resources\layout\text_with_help.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<XamarinComponentReference Include="googleplayservicesfroyo">
|
|
||||||
<Version>9.0</Version>
|
|
||||||
<Visible>False</Visible>
|
|
||||||
</XamarinComponentReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable\ic_storage_skydrive.png" />
|
<AndroidResource Include="Resources\drawable\ic_storage_skydrive.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -981,4 +985,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\device_access_new_account_holodark.png" />
|
<AndroidResource Include="Resources\drawable-hdpi\device_access_new_account_holodark.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<XamarinComponentReference Include="googleplayservices">
|
||||||
|
<Version>19.0.0</Version>
|
||||||
|
<Visible>False</Visible>
|
||||||
|
</XamarinComponentReference>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
Loading…
Reference in New Issue
Block a user