request permissions at runtime

use externalFilesDir when creating a database to make sure we don't need permissions here
revert Support Design library to previous version, had issues with 23.x
This commit is contained in:
Philipp Crocoll
2016-01-04 01:58:54 +01:00
parent 1690aff45e
commit a10a92d58d
22 changed files with 221 additions and 76 deletions

View File

@@ -3,18 +3,25 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security;
using Android;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Java.Security.Cert;
using Java.IO;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using File = System.IO.File;
using FileNotFoundException = System.IO.FileNotFoundException;
using IOException = System.IO.IOException;
namespace keepass2android.Io
{
public class BuiltInFileStorage: IFileStorage
public class BuiltInFileStorage : IFileStorage, IPermissionRequestingFileStorage
{
private const string PermissionGrantedKey = "PermissionGranted";
public enum CertificateProblem :long
{
CertEXPIRED = 0x800B0101,
@@ -112,7 +119,8 @@ namespace keepass2android.Io
private void ConvertException(IOConnectionInfo ioc, WebException ex)
{
if ((ex.Response is HttpWebResponse) && (((HttpWebResponse) ex.Response).StatusCode == HttpStatusCode.NotFound))
var response = ex.Response as HttpWebResponse;
if ((response != null) && (response.StatusCode == HttpStatusCode.NotFound))
{
throw new FileNotFoundException(ex.Message, ioc.Path, ex);
}
@@ -241,6 +249,26 @@ namespace keepass2android.Io
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode, bool alwaysReturnSuccess)
{
//check if we need to request the external-storage-permission at runtime
if (ioc.IsLocalFile())
{
bool requiresPermission = !ioc.Path.StartsWith(activity.Activity.FilesDir.CanonicalPath);
var extDirectory = activity.Activity.GetExternalFilesDir(null);
if ((extDirectory != null) && (ioc.Path.StartsWith(extDirectory.CanonicalPath)))
requiresPermission = false;
if (requiresPermission && (Build.VERSION.SdkInt >= BuildVersionCodes.M))
{
if (activity.Activity.CheckSelfPermission(Manifest.Permission.WriteExternalStorage) ==
Permission.Denied)
{
activity.StartFileUsageProcess(ioc, requestCode, alwaysReturnSuccess);
return;
}
}
}
Intent intent = new Intent();
activity.IocToIntent(intent, ioc);
activity.OnImmediateResult(requestCode, (int) FileStorageResults.FileUsagePrepared, intent);
@@ -251,24 +279,42 @@ namespace keepass2android.Io
//nothing to do, we're ready to go
}
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
public void OnCreate(IFileStorageSetupActivity fileStorageSetupActivity, Bundle savedInstanceState)
{
throw new NotImplementedException();
((Activity)fileStorageSetupActivity).RequestPermissions(new[] { Manifest.Permission.WriteExternalStorage }, 0);
}
public void OnResume(IFileStorageSetupActivity activity)
{
throw new NotImplementedException();
if (activity.State.ContainsKey(PermissionGrantedKey))
{
if (activity.State.GetBoolean(PermissionGrantedKey))
{
Intent data = new Intent();
data.PutExtra(FileStorageSetupDefs.ExtraIsForSave, activity.IsForSave);
data.PutExtra(FileStorageSetupDefs.ExtraPath, IocToPath(activity.Ioc));
((Activity) activity).SetResult((Result) FileStorageResults.FileUsagePrepared, data);
((Activity) activity).Finish();
}
else
{
Intent data = new Intent();
data.PutExtra(FileStorageSetupDefs.ExtraErrorMessage, "Permission denied. Please grant file access permission for this app.");
((Activity)activity).SetResult(Result.Canceled, data);
((Activity)activity).Finish();
}
}
}
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)
@@ -310,7 +356,7 @@ namespace keepass2android.Io
{
//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))
using (var writer = new FileOutputStream(ioc.Path, true))
{
writer.Close();
return false; //we can write
@@ -358,5 +404,11 @@ namespace keepass2android.Io
return false;
}
}
public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode,
string[] permissions, Permission[] grantResults)
{
fileStorageSetupActivity.State.PutBoolean(PermissionGrantedKey, grantResults[0] == Permission.Granted);
}
}
}

View File

@@ -5,6 +5,7 @@ using System.Net;
using System.Security.Cryptography;
using System.Text;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using KeePassLib.Cryptography;
using KeePassLib.Serialization;
@@ -59,7 +60,7 @@ namespace keepass2android.Io
/// Implements the IFileStorage interface as a proxy: A base storage is used as a remote storage. Local files are used to cache the
/// files on remote.
/// </summary>
public class CachingFileStorage : IFileStorage, IOfflineSwitchable
public class CachingFileStorage : IFileStorage, IOfflineSwitchable, IPermissionRequestingFileStorage
{
protected readonly OfflineSwitchableFileStorage _cachedStorage;
@@ -618,5 +619,11 @@ namespace keepass2android.Io
get { return _cachedStorage.IsOffline; }
set { _cachedStorage.IsOffline = value; }
}
public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode,
string[] permissions, Permission[] grantResults)
{
_cachedStorage.OnRequestPermissionsResult(fileStorageSetupActivity, requestCode, permissions, grantResults);
}
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using KeePassLib.Serialization;
@@ -22,7 +23,7 @@ namespace keepass2android.Io
public static String ExtraProcessName = "EXTRA_PROCESS_NAME";
public static String ExtraAlwaysReturnSuccess = "EXTRA_ALWAYS_RETURN_SUCCESS";
public static String ExtraPath = "PATH";
public static String ExtraPath = "fileName"; //match KP2A PasswordActivity Ioc-Path Extra key
public static String ExtraIsForSave = "IS_FOR_SAVE";
public static String ExtraErrorMessage = "EXTRA_ERROR_MESSAGE";
@@ -170,6 +171,11 @@ namespace keepass2android.Io
bool IsReadOnly(IOConnectionInfo ioc);
}
public interface IPermissionRequestingFileStorage
{
void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode, string[] permissions, Permission[] grantResults);
}
public interface IWriteTransaction: IDisposable
{
Stream OpenFile();

View File

@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using KeePassLib.Serialization;
using KeePassLib.Utility;
@@ -16,7 +17,7 @@ using FileNotFoundException = Java.IO.FileNotFoundException;
namespace keepass2android.Io
{
#if !EXCLUDE_JAVAFILESTORAGE
public abstract class JavaFileStorage: IFileStorage
public abstract class JavaFileStorage: IFileStorage, IPermissionRequestingFileStorage
{
protected string Protocol { get { return _jfs.ProtocolId; } }
@@ -356,6 +357,12 @@ namespace keepass2android.Io
return ioc.Path;
}
public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode,
string[] permissions, Permission[] grantResults)
{
_jfs.OnRequestPermissionsResult(((IJavaFileStorageFileStorageSetupActivity) fileStorageSetupActivity), requestCode,
permissions, grantResults.Select(p => (int)p).ToArray());
}
}
#endif
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using KeePassLib.Serialization;
@@ -16,7 +17,7 @@ namespace keepass2android.Io
/// Encapsulates another IFileStorage. Allows to switch to offline mode by throwing
/// an exception when trying to read or write a file.
/// </summary>
public class OfflineSwitchableFileStorage : IFileStorage, IOfflineSwitchable
public class OfflineSwitchableFileStorage : IFileStorage, IOfflineSwitchable, IPermissionRequestingFileStorage
{
private readonly IFileStorage _baseStorage;
public bool IsOffline { get; set; }
@@ -179,6 +180,15 @@ namespace keepass2android.Io
{
return _baseStorage.IsReadOnly(ioc);
}
public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode,
string[] permissions, Permission[] grantResults)
{
if (_baseStorage is IPermissionRequestingFileStorage)
{
((IPermissionRequestingFileStorage)_baseStorage).OnRequestPermissionsResult(fileStorageSetupActivity, requestCode, permissions, grantResults);
}
}
}
public class OfflineModeException : Exception

View File

@@ -12,7 +12,8 @@
<FileAlignment>512</FileAlignment>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v6.0</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>