Compare commits

..

33 Commits

Author SHA1 Message Date
Philipp Crocoll
533d92509f fix crowdin.yml file: adjust to renamed folder 2025-02-11 16:03:53 +01:00
PhilippC
46194317a8 Merge pull request #2766 from PhilippC/bugfix/potential-crashes-when-registering-receivers
fix potential crashes
2025-02-11 13:54:23 +01:00
PhilippC
c4d6e18759 Merge pull request #2765 from PhilippC/bugfix/2371-autofill-not-working-with-compose-apps
Fix autofill with Compose apps
2025-02-11 13:21:11 +01:00
Philipp Crocoll
ee41a600b1 fix potential crashes on old Android versions when registering receivers (after upgrade to support target sdk 34) 2025-02-11 13:18:23 +01:00
PhilippC
07562cc5a9 Merge pull request #2744 from midnight-wonderer/bug/hostname-matching
Fix hostname matching logic. closes #1926
2025-02-11 12:33:49 +01:00
Philipp Crocoll
0f5b411dc7 Fix an issue that autofill didn't work with compose apps as described on #2371. Closes #2371. Closes #2590 which is a prototype for this commit. 2025-02-11 12:33:14 +01:00
PhilippC
7577e3064c Merge pull request #2764 from PhilippC/remove-unused-package
remove no-longer required package from dependencies
2025-02-11 12:07:21 +01:00
Philipp Crocoll
d33e1f266c remove no-longer required package from dependencies 2025-02-11 11:12:25 +01:00
PhilippC
aeda21f163 Merge pull request #2762 from PhilippC/i18n-fix
I18n fix for password hint
2025-02-11 08:19:05 +01:00
Philipp Crocoll
4d1142df4d Merge branch 'password-hint-fix' into i18n-fix 2025-02-06 13:03:22 +01:00
Philipp Crocoll
4c632d0c72 fix i18n of a password hint in entry edit 2025-02-06 13:02:28 +01:00
PhilippC
cd75d7cda8 Merge pull request #2761 from PhilippC/feature/target-sdk-34
upgrade to target sdk 34: mark OngoingNotificationsService as Use, register receivers as exported
2025-02-06 12:42:02 +01:00
Philipp Crocoll
deb3701ebf Merge branch 'master' into i18n-fix 2025-02-06 12:30:04 +01:00
PhilippC
2bd0d415b6 Merge pull request #2758 from jonalmeida/patch-1
Remove deprecated Firefox Nightly app ID from trusted browsers
2025-02-06 12:17:20 +01:00
Philipp Crocoll
0bd58bd51f upgrade to target sdk 34: mark OngoingNotificationsService as Special Use, register receivers as exported
(This is required because it is allowed that plugins send intents and also intents from system notifications are not received in NonExported mode)
2025-02-06 12:12:54 +01:00
Philipp Crocoll
d998e08b4f make CreateDatabaseActivity use the blue theme to be more consistent 2025-02-06 11:19:29 +01:00
PhilippC
60e5dc21f1 Merge pull request #2760 from PhilippC/update-onedrive-implementation
Update onedrive implementation
2025-02-06 11:17:28 +01:00
Philipp Crocoll
793341918b cleanup code and fix bug with "My files" variant 2025-02-06 10:40:36 +01:00
Jonathan Almeida
8759fe5346 Remove deprecated Firefox Nightly app ID from trusted browsers
This app ID has not been used for a few years now and can be safely removed. 

See the original PR for added context: https://github.com/PhilippC/keepass2android/pull/896
2025-02-03 18:05:10 -05:00
PhilippC
1b8c0b8a70 Merge pull request #2754 from anttiharju/fix-push-branch-name
Use the right branch name
2025-01-30 10:37:02 +01:00
Philipp Crocoll
087af49514 fix OneDrive implementation for app folder 2025-01-30 09:09:25 +01:00
anttiharju
05e270bde3 Use the right branch name 2025-01-28 22:19:56 +02:00
Philipp Crocoll
7c6ce14348 this is the first attempt to fix the currently broken OneDrive implemenation by upgrading to Graph SDK 5. Unfortunately, the SDK is much harder to use because of a missing abstraction for DriveItems on different places (regular folder, special folder, shared folder) and also doesn't work well, see e.g. https://stackoverflow.com/questions/79374963/list-shared-folders-with-graph-sdk or https://stackoverflow.com/questions/79374845/graph-sdk-c-missing-methods-for-working-with-app-folder. 2025-01-21 16:23:07 +01:00
PhilippC
fdfa2b0bf2 Merge pull request #2747 from dktapps/patch-1
fix readme logo
2025-01-21 07:53:18 +01:00
Dylan T.
3fde725657 fix readme logo 2025-01-15 00:16:52 +00:00
PhilippC
146f181682 Merge pull request #2745 from PhilippC/argon2-fix
fix issue with argon2 kdf
2025-01-14 16:50:08 +01:00
PhilippC
1094b43009 Merge pull request #2746 from PhilippC/bugfix/refresh-number-of-entries-in-group
fix: number of entries in group did not immediately update
2025-01-14 16:49:54 +01:00
Philipp Crocoll
379d43e2b8 Merge remote-tracking branch 'remotes/origin/update-libs-and-tools' 2025-01-14 15:30:46 +01:00
Philipp Crocoll
090bc6249a fix: number of entries in group did not immediately update when adding entries 2025-01-14 15:29:22 +01:00
Sarun Rattanasiri
980df2b3a7 fix hostname matching logic 2025-01-14 17:24:48 +07:00
PhilippC
830494851d Merge pull request #2734 from anttiharju/prevent-duplicate-CI-runs
Prevent duplicate CI runs in PRs
2024-12-31 11:34:24 +01:00
anttiharju
6e30dd35ee Prevent duplicate runs in PRs 2024-12-20 08:11:26 +02:00
快乐小牛
f001d1fa54 fix: entry_edit hint i18n 2024-10-30 11:48:23 +08:00
26 changed files with 470 additions and 271 deletions

View File

@@ -1,6 +1,10 @@
name: Build keepass2android app
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
jobs:
# macos:

View File

@@ -1,7 +1,7 @@
files:
- source: src/keepass2android/Resources/values/strings.xml
- source: src/keepass2android-app/Resources/values/strings.xml
translation: >-
/src/keepass2android/Resources/values-%two_letters_code%/%original_file_name%
/src/keepass2android-app/Resources/values-%two_letters_code%/%original_file_name%
translate_attributes: '0'
content_segmentation: '0'
languages_mapping:

View File

@@ -1,4 +1,4 @@
<h1 align="center"><img src="/src/keepass2android/Resources/mipmap-xxxhdpi/ic_launcher_online.png" align="center" width="100" alt="Keepass2Android Logo">Keepass2Android</h1>
<h1 align="center"><img src="/src/keepass2android-app/Resources/mipmap-xxxhdpi/ic_launcher_online.png" align="center" width="100" alt="Keepass2Android Logo">Keepass2Android</h1>
# What is Keepass2Android?

View File

@@ -730,7 +730,7 @@ namespace Kp2aAutofillParser
{
public List<TField> InputFields { get; set; } = new List<TField>();
public string PackageId { get; set; } = null;
public string? PackageId { get; set; } = null;
public string WebDomain { get; set; } = null;
}

View File

@@ -1,25 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Util;
using Java.Net;
using keepass2android.Io.ItemLocation;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Graph.Drives.Item.Items.Item.CreateUploadSession;
using Microsoft.Graph.Models;
using Microsoft.Identity.Client;
using Newtonsoft.Json;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Authentication;
using DriveItemRequestBuilder = Microsoft.Graph.Drives.Item.Items.Item.DriveItemItemRequestBuilder;
using Exception = System.Exception;
using File = Microsoft.Graph.File;
using String = System.String;
namespace keepass2android.Io
@@ -45,7 +41,7 @@ namespace keepass2android.Io
}
public class OneDrive2ItemLocation<OneDrive2PrefixContainerType> where OneDrive2PrefixContainerType: OneDrive2PrefixContainer, new()
public class OneDrive2ItemLocation<OneDrive2PrefixContainerType> where OneDrive2PrefixContainerType : OneDrive2PrefixContainer, new()
{
public User User { get; set; } = new User();
@@ -59,7 +55,7 @@ namespace keepass2android.Io
{
get
{
OneDrive2ItemLocation< OneDrive2PrefixContainerType> copy = OneDrive2ItemLocation< OneDrive2PrefixContainerType>.FromString(this.ToString());
OneDrive2ItemLocation<OneDrive2PrefixContainerType> copy = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(this.ToString());
if (copy.LocalPath.Any())
{
//pop last:
@@ -180,7 +176,7 @@ namespace keepass2android.Io
public abstract class OneDrive2FileStorage<OneDrive2PrefixContainerType> : IFileStorage where OneDrive2PrefixContainerType: OneDrive2PrefixContainer, new()
public abstract class OneDrive2FileStorage<OneDrive2PrefixContainerType> : IFileStorage where OneDrive2PrefixContainerType : OneDrive2PrefixContainer, new()
{
public static IPublicClientApplication _publicClientApp = null;
@@ -201,21 +197,114 @@ namespace keepass2android.Io
class PathItemBuilder
{
private readonly string _specialFolder;
public IGraphServiceClient client;
private readonly string? _specialFolder;
public GraphServiceClient client;
public OneDrive2ItemLocation<OneDrive2PrefixContainerType> itemLocation;
public bool verbose;
public PathItemBuilder(string specialFolder)
public PathItemBuilder(string? specialFolder)
{
_specialFolder = specialFolder;
}
public IDriveItemRequestBuilder getPathItem()
/// <summary>
/// Wraps the different DriveItemRequestBuilder classes and allows accessing the different types easily
/// </summary>
/// NOTE: even though CustomDriveItemItemRequestBuilder derives from Kp2aDriveItemRequestBuilder, we cannot use polymorphism here because the methods are declared with "new".
/// If you cast assign an CustomDriveItemItemRequestBuilder object to a variable declared as Kp2aDriveItemRequestBuilder and then call a method on it, it will fail.
public class DriveItemRequestBuilderWrapper
{
Kp2aLog.Log("getPathItem for " + itemLocation.ToString());
IDriveItemRequestBuilder pathItem;
public class DriveItemRequestBuilderResult<T>
{
private readonly DriveItemRequestBuilderWrapper _req;
public DriveItemRequestBuilderResult(DriveItemRequestBuilderWrapper req)
{
_req = req;
}
public Task<T?> Result { get; set; }
public DriveItemRequestBuilderResult<T> ForDriveItemRequestBuilder(Func<DriveItemRequestBuilder, Task<T?>> action)
{
if (_req.DriveItemRequestBuilder != null)
{
Result = action(_req.DriveItemRequestBuilder);
}
return this;
}
public DriveItemRequestBuilderResult<T> ForCustomDriveItemRequestBuilder(Func<CustomDriveItemItemRequestBuilder, Task<T?>> action)
{
if (_req.CustomDriveItemRequestBuilder != null)
{
Result = action(_req.CustomDriveItemRequestBuilder);
}
return this;
}
}
public class DriveItemRequestBuilderAsyncTask
{
private readonly DriveItemRequestBuilderWrapper _req;
public DriveItemRequestBuilderAsyncTask(DriveItemRequestBuilderWrapper req)
{
_req = req;
Task = Task.CompletedTask;
}
public Task Task { get; private set; }
public DriveItemRequestBuilderAsyncTask ForDriveItemRequestBuilder(Func<DriveItemRequestBuilder, Task> action)
{
if (_req.DriveItemRequestBuilder != null)
{
Task = action(_req.DriveItemRequestBuilder);
}
return this;
}
public DriveItemRequestBuilderAsyncTask ForCustomDriveItemRequestBuilder(Func<CustomDriveItemItemRequestBuilder, Task> action)
{
if (_req.CustomDriveItemRequestBuilder != null)
{
Task = action(_req.CustomDriveItemRequestBuilder);
}
return this;
}
}
public DriveItemRequestBuilder? DriveItemRequestBuilder { get; set; }
public CustomDriveItemItemRequestBuilder? CustomDriveItemRequestBuilder { get; set; }
public DriveItemRequestBuilderResult<T> ToAsyncResult<T>()
{
return new DriveItemRequestBuilderResult<T>(this);
}
public DriveItemRequestBuilderAsyncTask ToAsyncTask()
{
return new DriveItemRequestBuilderAsyncTask(this);
}
};
public async Task<DriveItemRequestBuilderWrapper> BuildPathItemAsync()
{
Kp2aLog.Log("buildPathItem for " + itemLocation.ToString());
DriveItemRequestBuilderWrapper result = new DriveItemRequestBuilderWrapper();
if (!hasShare())
{
throw new Exception("Cannot get path item without share");
@@ -228,56 +317,53 @@ namespace keepass2android.Io
if (_specialFolder == null)
{
if (verbose) Kp2aLog.Log("No special folder. Use drive root.");
pathItem = client.Me.Drive.Root;
}
else
{
if (verbose) Kp2aLog.Log("Special folder = " + _specialFolder);
pathItem = client.Me.Drive.Special[_specialFolder];
}
if (itemLocation.LocalPath.Any())
{
if (verbose) Kp2aLog.Log("LocalPath = " + itemLocation.LocalPathString);
pathItem = pathItem.ItemWithPath(itemLocation.LocalPathString);
result.CustomDriveItemRequestBuilder = client.Drives[itemLocation.DriveId].Root
.ItemWithPath(itemLocation.LocalPathString);
}
else
{
result.DriveItemRequestBuilder = client.Drives[itemLocation.DriveId].Items["root"];
}
}
else
{
if (verbose) Kp2aLog.Log("Special folder = " + _specialFolder);
DriveItemRequestBuilder specialRoot = client.Drives[itemLocation.DriveId].Items[_specialFolder];
if (itemLocation.LocalPath.Any())
{
result.CustomDriveItemRequestBuilder = specialRoot.ItemWithPath(itemLocation.LocalPathString);
}
else
{
result.DriveItemRequestBuilder = specialRoot;
}
}
}
else
{
if (verbose) Kp2aLog.Log("Path share is not me");
if (!itemLocation.LocalPath.Any())
{
String webUrl = itemLocation.Share.WebUrl;
if (verbose) Kp2aLog.Log("Share WebUrl = " + webUrl);
var encodedShareId = CalculateEncodedShareId(webUrl);
return client.Shares[encodedShareId].Root;
result.DriveItemRequestBuilder = client.Drives[itemLocation.DriveId].Items[itemLocation.Share.Id];
return result;
}
/*String webUrl = itemLocation.Share.WebUrl;
if ("".Equals(itemLocation.LocalPath) == false)
{
if (!webUrl.EndsWith("/")) webUrl += "/";
webUrl += itemLocation.LocalPath;
}
Android.Util.Log.Debug("KP2A","webUrl = " + Encoding.UTF8.GetBytes(webUrl));
//calculate shareid according to https://docs.microsoft.com/en-us/graph/api/shares-get?view=graph-rest-1.0&tabs=java
var encodedShareId = CalculateEncodedShareId(webUrl);
Android.Util.Log.Debug("KP2A", "encodedShareId = " + encodedShareId);
pathItem = client.Shares[encodedShareId].Root;
*/
if (verbose) Kp2aLog.Log("Using driveId=" + itemLocation.DriveId + " and item id=" + itemLocation.LocalPath.Last().Id);
return client.Drives[itemLocation.DriveId].Items[itemLocation.LocalPath.Last().Id];
result.DriveItemRequestBuilder = client.Drives[itemLocation.DriveId].Items[itemLocation.LocalPath.Last().Id];
}
return pathItem;
}
private static string CalculateEncodedShareId(string webUrl)
{
String encodedShareId = "u!" + Base64.EncodeToString(Encoding.UTF8.GetBytes(webUrl),
Base64Flags.NoPadding).Replace('/', '_').Replace('+', '_')
.Replace("\n", ""); //encodeToString adds a newline character add the end - remove
return encodedShareId;
return result;
}
public bool hasShare()
@@ -289,6 +375,7 @@ namespace keepass2android.Io
{
return itemLocation.LocalPath.Any();
}
}
private string protocolId;
@@ -310,26 +397,23 @@ namespace keepass2android.Io
get { yield return ProtocolId; }
}
class GraphServiceClientWithState
protected class GraphServiceClientWithState
{
public IGraphServiceClient Client { get; set; }
public GraphServiceClient Client { get; set; }
public DateTime TokenExpiryDate { get; set; }
public bool RequiresUserInteraction { get; set; }
}
readonly Dictionary<String /*userid*/, GraphServiceClientWithState> mClientByUser =
new Dictionary<String /*userid*/, GraphServiceClientWithState>();
protected readonly Dictionary<String /*userid*/, GraphServiceClientWithState> _mClientByUser = new();
private async Task<IGraphServiceClient> TryGetMsGraphClient(String path, bool tryConnect)
private async Task<GraphServiceClient> TryGetMsGraphClient(String path, bool tryConnect)
{
String userId = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(path).User.Id;
string userId = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(path).User.Id;
logDebug("TryGetMsGraphClient for " + userId);
if (mClientByUser.ContainsKey(userId))
if (_mClientByUser.TryGetValue(userId, out var clientWithState))
{
logDebug("TryGetMsGraphClient found user " + userId);
GraphServiceClientWithState clientWithState = mClientByUser[userId];
if (!(clientWithState.RequiresUserInteraction || (clientWithState.TokenExpiryDate < DateTime.Now) ||
(clientWithState.Client == null)))
{
@@ -348,7 +432,7 @@ namespace keepass2android.Io
if (await TryLoginSilent(path) != null)
{
logDebug("trying to connect ok");
return mClientByUser[userId].Client;
return _mClientByUser.GetValueOrDefault(userId, null).Client;
}
logDebug("trying to connect failed");
}
@@ -356,25 +440,33 @@ namespace keepass2android.Io
return null;
}
public class TokenFromAuthResultProvider : IAccessTokenProvider
{
public AuthenticationResult AuthenticationResult
{
get;
set;
}
public async Task<string> GetAuthorizationTokenAsync(Uri uri, Dictionary<string, object>? additionalAuthenticationContext = null,
CancellationToken cancellationToken = new CancellationToken())
{
return AuthenticationResult.AccessToken;
}
private IGraphServiceClient BuildClient(AuthenticationResult authenticationResult)
public AllowedHostsValidator AllowedHostsValidator { get; }
}
private GraphServiceClient BuildClient(AuthenticationResult authenticationResult)
{
logDebug("buildClient...");
//DeviceCodeProvider authenticationProvider = new DeviceCodeProvider(_publicClientApp, Scopes);
var authenticationProvider = new DelegateAuthenticationProvider(
(requestMessage) =>
{
var access_token = authenticationResult.AccessToken;
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", access_token);
return Task.FromResult(0);
});
var authenticationProvider = new BaseBearerTokenAuthenticationProvider(new TokenFromAuthResultProvider() { AuthenticationResult = authenticationResult });
GraphServiceClientWithState clientWithState = new GraphServiceClientWithState()
{
Client = new GraphServiceClient(authenticationProvider),
Client = new GraphServiceClient(new HttpClient(), authenticationProvider),
RequiresUserInteraction = false,
TokenExpiryDate = authenticationResult.ExpiresOn.LocalDateTime
};
@@ -383,13 +475,12 @@ namespace keepass2android.Io
if (authenticationResult.Account == null)
throw new Exception("authenticationResult.Account == null!");
mClientByUser[authenticationResult.Account.HomeAccountId.Identifier] = clientWithState;
_mClientByUser[authenticationResult.Account.HomeAccountId.Identifier] = clientWithState;
logDebug("buildClient ok.");
return clientWithState.Client;
}
private void logDebug(string str)
{
#if DEBUG
@@ -398,20 +489,25 @@ namespace keepass2android.Io
}
protected abstract string SpecialFolder { get; }
protected abstract Task<string?> GetSpecialFolder(
OneDrive2ItemLocation<OneDrive2PrefixContainerType> itemLocation, GraphServiceClient client);
private async Task<PathItemBuilder> GetPathItemBuilder(String path)
{
PathItemBuilder result = new PathItemBuilder(SpecialFolder);
var itemLocation = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(path);
var client = await TryGetMsGraphClient(path, true);
result.itemLocation = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(path);
PathItemBuilder result = new PathItemBuilder(await GetSpecialFolder(itemLocation, client));
result.itemLocation = itemLocation;
if (string.IsNullOrEmpty(result.itemLocation.User?.Name))
{
throw new Exception("path does not contain user");
}
result.client = await TryGetMsGraphClient(path, true);
result.client = client;
if (result.client == null)
throw new Exception("Failed to connect or authenticate to OneDrive!");
@@ -422,7 +518,7 @@ namespace keepass2android.Io
}
private Exception convertException(ClientException e)
private Exception convertException(ServiceException e)
{
if (e.IsMatch(GraphErrorCode.ItemNotFound.ToString()))
@@ -436,8 +532,8 @@ namespace keepass2android.Io
private Exception convertException(Exception e)
{
if (e is ClientException)
return convertException((ClientException)e);
if (e is ServiceException)
return convertException((ServiceException)e);
if (e is AggregateException aggregateException)
{
foreach (var inner in aggregateException.InnerExceptions)
@@ -449,8 +545,6 @@ namespace keepass2android.Io
return e;
}
public bool UserShouldBackup
{
get { return false; }
@@ -464,9 +558,11 @@ namespace keepass2android.Io
Task.Run(async () =>
{
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(ioc.Path);
await pathItemBuilder.getPathItem()
.Request()
.DeleteAsync();
PathItemBuilder.DriveItemRequestBuilderWrapper pathItem = await pathItemBuilder.BuildPathItemAsync();
await pathItem.ToAsyncTask()
.ForDriveItemRequestBuilder(builder => builder.DeleteAsync())
.ForCustomDriveItemRequestBuilder(b => b.DeleteAsync())
.Task;
}).Wait();
}
catch (Exception e)
@@ -492,15 +588,21 @@ namespace keepass2android.Io
string path = ioc.Path;
logDebug("openFileForRead. Path=" + path);
Stream result = Task.Run(async () =>
Stream? result = Task.Run(async () =>
{
PathItemBuilder clientAndpath = await GetPathItemBuilder(path);
return await clientAndpath
.getPathItem()
.Content
.Request()
.GetAsync();
logDebug("openFileForRead. Path=" + path);
PathItemBuilder clientAndPath = await GetPathItemBuilder(path);
return await (await clientAndPath.BuildPathItemAsync())
.ToAsyncResult<Stream>()
.ForDriveItemRequestBuilder((b) => b.Content.GetAsync())
.ForCustomDriveItemRequestBuilder(b => b.Content.GetAsync())
.Result;
}).Result;
if (result == null)
throw new Exception("failed to open stream");
logDebug("ok");
return result;
@@ -551,22 +653,23 @@ namespace keepass2android.Io
{
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(path);
//for small files <2MB use the direct upload:
if (stream.Length < 2* 1024 * 1024)
if (stream.Length < 2 * 1024 * 1024)
{
return await
pathItemBuilder
.getPathItem()
.Content
.Request()
.PutAsync<DriveItem>(stream);
await
(await pathItemBuilder
.BuildPathItemAsync())
.ToAsyncTask()
.ForDriveItemRequestBuilder((b) => b.Content.PutAsync(stream))
.ForCustomDriveItemRequestBuilder(b => b.Content.PutAsync(stream))
.Task;
return;
}
//for larger files use an upload session. This is required for 4MB and beyond, but as the docs are not very clear about this
//limit, let's use it a bit more often to be safe.
var uploadProps = new DriveItemUploadableProperties
var uploadProps = new CreateUploadSessionPostRequestBody
{
ODataType = null,
AdditionalData = new Dictionary<string, object>
{
{ "@microsoft.graph.conflictBehavior", "replace" }
@@ -574,11 +677,13 @@ namespace keepass2android.Io
};
var uploadSession = await pathItemBuilder
.getPathItem()
.CreateUploadSession(uploadProps)
.Request()
.PostAsync();
var uploadSession = await (await pathItemBuilder
.BuildPathItemAsync())
.ToAsyncResult<UploadSession>()
.ForDriveItemRequestBuilder(b => b.CreateUploadSession.PostAsync(uploadProps))
.ForCustomDriveItemRequestBuilder(b => b.CreateUploadSession.PostAsync(uploadProps))
.Result;
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
@@ -590,21 +695,12 @@ namespace keepass2android.Io
throw new Exception("Failed to upload data!");
}
return uploadResult.ItemResponse;
}).Wait();
}
catch (Exception e)
{
Task.Run(async () =>
{
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(path);
pathItemBuilder.verbose = true;
pathItemBuilder.getPathItem();
}).Wait();
throw convertException(e);
}
}
@@ -627,7 +723,7 @@ namespace keepass2android.Io
private string GetFilename(string path)
{
string localPath = "/"+OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(path).LocalPathString;
string localPath = "/" + OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(path).LocalPathString;
return localPath.Substring(localPath.LastIndexOf("/", StringComparison.Ordinal) + 1);
}
@@ -649,10 +745,11 @@ namespace keepass2android.Io
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(parentIoc.Path);
return await pathItemBuilder.getPathItem()
.Children
.Request()
.AddAsync(driveItem);
return await (await pathItemBuilder.BuildPathItemAsync())
.ToAsyncResult<DriveItem>()
.ForDriveItemRequestBuilder(b => b.Children.PostAsync(driveItem))
.ForCustomDriveItemRequestBuilder(b => b.Children.PostAsync(driveItem))
.Result;
}).Result;
@@ -681,6 +778,8 @@ namespace keepass2android.Io
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(ioc.Path);
logDebug("listing files for " + ioc.Path);
OneDrive2ItemLocation<OneDrive2PrefixContainerType> itemLocation = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(ioc.Path);
var client = await TryGetMsGraphClient(ioc.Path, true);
if (!pathItemBuilder.hasShare() && !pathItemBuilder.hasOneDrivePath())
{
logDebug("listing shares.");
@@ -689,35 +788,34 @@ namespace keepass2android.Io
logDebug("listing regular children.");
List<FileDescription> result = new List<FileDescription>();
/*logDebug("parent before:" + parentPath);
parentPath = parentPath.substring(getProtocolPrefix().length());
logDebug("parent after: " + parentPath);*/
IDriveItemChildrenCollectionPage itemsPage = await pathItemBuilder.getPathItem()
.Children
.Request()
.GetAsync();
while (true)
{
IList<DriveItem> items = itemsPage.CurrentPage;
if (!items.Any())
return result;
var driveItems = await GetDriveItems(pathItemBuilder);
foreach (DriveItem i in items)
if (driveItems != null)
foreach (DriveItem? i in driveItems)
{
var e = GetFileDescription(pathItemBuilder.itemLocation.BuildLocalChildLocation(i.Name, i.Id, i.ParentReference?.DriveId), i);
var e = GetFileDescription(itemLocation.BuildLocalChildLocation(i.Name, i.Id, i.ParentReference?.DriveId), i);
result.Add(e);
}
var nextPageReqBuilder = itemsPage.NextPageRequest;
if (nextPageReqBuilder == null)
return result;
itemsPage = Task.Run(async () => await nextPageReqBuilder.GetAsync()).Result;
}
private async Task<List<DriveItem?>> GetDriveItems(
PathItemBuilder pathItemBuilder)
{
var pathItem = await pathItemBuilder.BuildPathItemAsync();
var response = await pathItem
.ToAsyncResult<DriveItemCollectionResponse>()
.ForDriveItemRequestBuilder(b => b.Children.GetAsync())
.ForCustomDriveItemRequestBuilder(b => b.Children.GetAsync())
.Result;
return response.Value;
}
private FileDescription GetFileDescription(OneDrive2ItemLocation<OneDrive2PrefixContainerType> path, DriveItem i)
private FileDescription GetFileDescription(OneDrive2ItemLocation<OneDrive2PrefixContainerType> path, DriveItem? i)
{
FileDescription e = new FileDescription();
if (i.Size != null)
@@ -740,7 +838,7 @@ namespace keepass2android.Io
{
try
{
return Task.Run(async() => await GetFileDescriptionAsync(ioc)).Result;
return Task.Run(async () => await GetFileDescriptionAsync(ioc)).Result;
}
catch (Exception e)
{
@@ -764,9 +862,11 @@ namespace keepass2android.Io
return rootEntry;
}
IDriveItemRequestBuilder pathItem = pathItemBuilder.getPathItem();
DriveItem item = await pathItem.Request().GetAsync();
var driveReq = await pathItemBuilder.BuildPathItemAsync();
DriveItem? item = await driveReq.ToAsyncResult<DriveItem>()
.ForDriveItemRequestBuilder(b => b.GetAsync())
.ForCustomDriveItemRequestBuilder(b => b.GetAsync())
.Result;
return GetFileDescription(pathItemBuilder.itemLocation, item);
}
@@ -783,7 +883,7 @@ namespace keepass2android.Io
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode,
string protocolId)
{
String path = ProtocolId+ "://";
String path = ProtocolId + "://";
activity.StartSelectFileProcess(IOConnectionInfo.FromPath(path), isForSave, requestCode);
}
@@ -827,7 +927,7 @@ namespace keepass2android.Io
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
{
if (!Task.Run(async() => await IsConnectedAsync(ioc.Path, true)).Result)
if (!Task.Run(async () => await IsConnectedAsync(ioc.Path, true)).Result)
{
throw new Exception("MsGraph login required");
}
@@ -846,7 +946,6 @@ namespace keepass2android.Io
protected void FinishActivityWithSuccess(
IFileStorageSetupActivity setupActivity)
{
//Log.d("KP2AJ", "Success with authenticating!");
Activity activity = (Activity)setupActivity;
if (setupActivity.ProcessName
@@ -948,8 +1047,6 @@ namespace keepass2android.Io
logDebug("AcquireTokenSilent ok.");
BuildClient(authResult);
/*User me = await graphClient.Me.Request().WithForceRefresh(true).GetAsync();
logDebug("received name " + me.DisplayName);*/
var rootFolder = BuildRootPathForUser(authResult);
logDebug("Found RootPath for user");
@@ -966,7 +1063,7 @@ namespace keepass2android.Io
};
mClientByUser[account.HomeAccountId.Identifier] = clientWithState;
_mClientByUser[account.HomeAccountId.Identifier] = clientWithState;
logDebug("ui required");
return null;
}
@@ -1002,7 +1099,7 @@ namespace keepass2android.Io
try
{
var itemLocation = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(ioc.Path);
string result = ProtocolId+ "://";
string result = ProtocolId + "://";
if (!string.IsNullOrEmpty(itemLocation.User?.Id))
{
result += itemLocation.User?.Name;
@@ -1029,71 +1126,78 @@ namespace keepass2android.Io
}
private async Task<List<FileDescription>> ListShares(OneDrive2ItemLocation<OneDrive2PrefixContainerType> parentPath, IGraphServiceClient client)
private async Task<List<FileDescription>> ListShares(OneDrive2ItemLocation<OneDrive2PrefixContainerType> parentPath, GraphServiceClient client)
{
List<FileDescription> result = new List<FileDescription>();
List<FileDescription> result = [];
var drives = (await client.Me.Drives.GetAsync()).Value;
if (drives != null)
{
drives.ForEach(drive =>
{
var e = new FileDescription()
{
DisplayName = GetDriveDisplayName(drive),
IsDirectory = true,
CanRead = true,
CanWrite = true,
Path = parentPath.BuildShare("me","me","me", drive.Id).ToString()
};
result.Add(e);
});
}
DriveItem root = await client.Me.Drive.Root.Request().GetAsync();
FileDescription myEntry = GetFileDescription(parentPath.BuildShare("me","me","me", root.ParentReference?.DriveId), root);
myEntry.DisplayName = MyOneDriveDisplayName;
string? driveId = parentPath.DriveId;
if ((string.IsNullOrEmpty(driveId)) && (drives?.Any() == true))
{
driveId = drives.First().Id;
}
result.Add(myEntry);
if (!CanListShares)
return result;
var sharedWithMeResponse = await client.Drives[driveId].SharedWithMe.GetAsSharedWithMeGetResponseAsync();
IDriveSharedWithMeCollectionPage sharedWithMeCollectionPage = await client.Me.Drive.SharedWithMe().Request().GetAsync();
while (true)
foreach (DriveItem i in sharedWithMeResponse?.Value ?? [])
{
IList<DriveItem> sharedWithMeItems = sharedWithMeCollectionPage.CurrentPage;
if (!sharedWithMeItems.Any())
break;
foreach (DriveItem i in sharedWithMeItems)
var oneDrive2ItemLocation = parentPath.BuildShare(i.RemoteItem.Id, i.RemoteItem.Name, i.RemoteItem.WebUrl, i.RemoteItem.ParentReference.DriveId);
FileDescription sharedFileEntry = new FileDescription()
{
FileDescription sharedFileEntry = GetFileDescription(parentPath.BuildShare(i.Id, i.Name, i.WebUrl, i.ParentReference?.DriveId), i);
CanWrite = true, CanRead = true, DisplayName = i.Name,
IsDirectory = true,
Path = oneDrive2ItemLocation.ToString()
};
result.Add(sharedFileEntry);
}
var b = sharedWithMeCollectionPage.NextPageRequest;
if (b == null) break;
sharedWithMeCollectionPage = await b.GetAsync();
}
return result;
}
protected virtual string GetDriveDisplayName(Drive drive)
{
return drive.Name ?? drive.DriveType ?? "(unnamed drive)";
}
protected virtual string MyOneDriveDisplayName { get { return "My OneDrive"; } }
public abstract bool CanListShares { get; }
DriveItem TryFindFile(PathItemBuilder parent, string filename)
async Task<DriveItem?> TryFindFileAsync(PathItemBuilder parent, string filename)
{
IDriveItemChildrenCollectionPage itemsPage = Task.Run(async () => await parent.getPathItem()
.Children
.Request()
.GetAsync()).Result;
while (true)
{
IList<DriveItem> items = itemsPage.CurrentPage;
if (!items.Any())
return null;
foreach (DriveItem i in items)
var driveItems = await GetDriveItems(parent);
if (driveItems != null)
foreach (DriveItem? i in driveItems)
{
if (i.Name == filename)
return i;
}
var nextPageReqBuilder = itemsPage.NextPageRequest;
if (nextPageReqBuilder == null)
return null;
itemsPage = Task.Run(async () => await nextPageReqBuilder.GetAsync()).Result;
}
return null;
}
@@ -1102,7 +1206,7 @@ namespace keepass2android.Io
{
try
{
return Task.Run(async() => await CreateFilePathAsync(parent, newFilename)).Result;
return Task.Run(async () => await CreateFilePathAsync(parent, newFilename)).Result;
}
catch (Exception e)
{
@@ -1116,7 +1220,7 @@ namespace keepass2android.Io
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(parent);
//see if such a file exists already:
var item = TryFindFile(pathItemBuilder, newFilename);
var item = await TryFindFileAsync(pathItemBuilder, newFilename);
if (item != null)
{
return pathItemBuilder.itemLocation.BuildLocalChildLocation(item.Name, item.Id, item.ParentReference?.DriveId)
@@ -1125,12 +1229,17 @@ namespace keepass2android.Io
//doesn't exist. Create:
logDebug("building request for " + pathItemBuilder.itemLocation);
DriveItem res = await pathItemBuilder.getPathItem()
.ItemWithPath(newFilename)
.Content
.Request()
.PutAsync<DriveItem>(new MemoryStream());
PathItemBuilder targetPathItemBuilder = await GetPathItemBuilder(pathItemBuilder.itemLocation.BuildLocalChildLocation(newFilename, "", pathItemBuilder.itemLocation.DriveId ?? "").ToString());
var emptyStream = new MemoryStream();
var driveItemReq = await targetPathItemBuilder.BuildPathItemAsync();
DriveItem? res = await driveItemReq
.ToAsyncResult<DriveItem>()
.ForDriveItemRequestBuilder(b => b.Content.PutAsync(emptyStream))
.ForCustomDriveItemRequestBuilder(b => b.Content.PutAsync(emptyStream))
.Result;
return pathItemBuilder.itemLocation.BuildLocalChildLocation(res.Name, res.Id, res.ParentReference?.DriveId)
.ToString();
@@ -1157,7 +1266,7 @@ namespace keepass2android.Io
}
}
public class OneDrive2FullFileStorage: OneDrive2FileStorage<OneDrive2FullPrefixContainer>
public class OneDrive2FullFileStorage : OneDrive2FileStorage<OneDrive2FullPrefixContainer>
{
public override IEnumerable<string> Scopes
{
@@ -1172,8 +1281,13 @@ namespace keepass2android.Io
}
}
protected override async Task<string?> GetSpecialFolder(
OneDrive2ItemLocation<OneDrive2FullPrefixContainer> itemLocation, GraphServiceClient client)
{
return null;
}
public override bool CanListShares { get { return true; } }
protected override string SpecialFolder { get { return null; } }
}
@@ -1190,8 +1304,14 @@ namespace keepass2android.Io
}
}
protected override async Task<string?> GetSpecialFolder(
OneDrive2ItemLocation<OneDrive2MyFilesPrefixContainer> itemLocation, GraphServiceClient client)
{
return null;
}
public override bool CanListShares { get { return false; } }
protected override string SpecialFolder { get { return null; } }
}
@@ -1209,8 +1329,41 @@ namespace keepass2android.Io
}
}
protected override string SpecialFolder { get { return "approot"; } }
protected override async Task<string?> GetSpecialFolder(
OneDrive2ItemLocation<OneDrive2AppFolderPrefixContainer> itemLocation, GraphServiceClient client)
{
if (string.IsNullOrEmpty(itemLocation.DriveId))
return null; //can happen if we are accessing the root
if (!_specialFolderIdByDriveId.ContainsKey(itemLocation.DriveId))
{
try
{
var specialFolder = await client.Drives[itemLocation.DriveId].Special[SpecialFolderName].GetAsync();
_specialFolderIdByDriveId[itemLocation.DriveId] = specialFolder.Id;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
return _specialFolderIdByDriveId[itemLocation.DriveId];
}
protected string SpecialFolderName { get { return "approot"; } }
private readonly Dictionary<string,string> _specialFolderIdByDriveId = new Dictionary<string, string>();
protected override string GetDriveDisplayName(Drive drive)
{
return drive.Name ?? MyOneDriveDisplayName;
}
public override bool CanListShares { get { return false; } }
protected override string MyOneDriveDisplayName { get { return "Keepass2Android App Folder"; } }
protected override string MyOneDriveDisplayName => "Keepass2Android App Folder";
}
}

View File

@@ -11,9 +11,9 @@
<ItemGroup>
<PackageReference Include="FluentFTP" Version="51.1.0" />
<PackageReference Include="MegaApiClient" Version="1.10.4" />
<PackageReference Include="Microsoft.Graph" Version="1.21.0" />
<PackageReference Include="Microsoft.Graph.Auth" Version="1.0.0-preview.7" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.49.1" />
<PackageReference Include="Microsoft.Graph" Version="5.68.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.1" />
<PackageReference Include="Xamarin.AndroidX.Browser" Version="1.8.0" />
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.13.1.5" />
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.11.0.3" />
</ItemGroup>

View File

@@ -138,7 +138,8 @@ namespace keepass2android
{
continue;
}
if (host.IndexOf(otherHost, StringComparison.InvariantCultureIgnoreCase) > -1)
if (string.Equals(host, otherHost, StringComparison.OrdinalIgnoreCase) ||
host.EndsWith("." + otherHost, StringComparison.OrdinalIgnoreCase))
{
pgResults.AddEntry(entry, false);
}

View File

@@ -90,6 +90,11 @@ namespace keepass2android
// Mark parent group dirty
_app.DirtyGroups.Add(parent);
// even mark the parent of the parent dirty to update the views showing the number of child entries
if (parent?.ParentGroup != null)
{
_app.DirtyGroups.Add(parent.ParentGroup);
}

View File

@@ -356,7 +356,13 @@ public class KP2AKeyboard extends InputMethodService
pFilter.addAction("android.intent.action.PACKAGE_ADDED");
pFilter.addAction("android.intent.action.PACKAGE_REPLACED");
pFilter.addAction("android.intent.action.PACKAGE_REMOVED");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
registerReceiver(mPluginManager, pFilter, RECEIVER_EXPORTED);
}
else
{
registerReceiver(mPluginManager, pFilter);
}
LatinIMEUtil.GCUtils.getInstance().reset();
@@ -375,16 +381,28 @@ public class KP2AKeyboard extends InputMethodService
// register to receive ringer mode changes for silent mode
IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
registerReceiver(mSilentModeReceiver, filter, RECEIVER_EXPORTED);
}
else
{
registerReceiver(mSilentModeReceiver, filter);
}
prefs.registerOnSharedPreferenceChangeListener(this);
//check if we have KP2A data available:
mHadKp2aData = mShowKp2aKeyboard = keepass2android.kbbridge.KeyboardData.hasData();
mHadKp2aData = mShowKp2aKeyboard = KeyboardData.hasData();
mClearKeyboardReceiver = new ClearKeyboardBroadcastReceiver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
registerReceiver(mClearKeyboardReceiver, new IntentFilter(get_KEEPASS2ANDROID_KEYBOARD_CLEARED(this)), RECEIVER_EXPORTED);
}
else
{
registerReceiver(mClearKeyboardReceiver, new IntentFilter(get_KEEPASS2ANDROID_KEYBOARD_CLEARED(this)));
android.util.Log.d("KP2AK", "registered receiver for clear keyboard broadcast: "+get_KEEPASS2ANDROID_KEYBOARD_CLEARED(this));
}
Log.d("KP2AK", "registered receiver for clear keyboard broadcast: "+get_KEEPASS2ANDROID_KEYBOARD_CLEARED(this));
}

View File

@@ -20,7 +20,7 @@ namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
Theme = "@style/Kp2aTheme_ActionBar")]
Theme = "@style/Kp2aTheme_BlueActionBar")]
public class CreateDatabaseActivity : LifecycleAwareActivity
{
private IOConnectionInfo _ioc;

View File

@@ -53,6 +53,7 @@ using keepass2android.fileselect;
using KeeTrayTOTP.Libraries;
using Boolean = Java.Lang.Boolean;
using Android.Util;
using AndroidX.Core.Content;
using Google.Android.Material.Dialog;
using keepass2android;
@@ -491,9 +492,9 @@ namespace keepass2android
App.Kp2a.LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2a.CurrentDb);
_pluginActionReceiver = new PluginActionReceiver(this);
RegisterReceiver(_pluginActionReceiver, new IntentFilter(Strings.ActionAddEntryAction));
ContextCompat.RegisterReceiver(this, _pluginActionReceiver, new IntentFilter(Strings.ActionAddEntryAction), (int)ReceiverFlags.Exported);
_pluginFieldReceiver = new PluginFieldReceiver(this);
RegisterReceiver(_pluginFieldReceiver, new IntentFilter(Strings.ActionSetEntryField));
ContextCompat.RegisterReceiver(this, _pluginFieldReceiver, new IntentFilter(Strings.ActionSetEntryField), (int)ReceiverFlags.Exported);
var notifyPluginsOnOpenThread = new Thread(NotifyPluginsOnOpen);
notifyPluginsOnOpenThread.Start();

View File

@@ -21,6 +21,7 @@ using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Views;
using AndroidX.Core.Content;
using KeePassLib.Serialization;
namespace keepass2android
@@ -69,7 +70,7 @@ namespace keepass2android
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
filter.AddAction(Intent.ActionScreenOff);
RegisterReceiver(_intentReceiver, filter);
ContextCompat.RegisterReceiver(this, _intentReceiver, filter, (int)ReceiverFlags.Exported);
}
protected override void OnDestroy()

View File

@@ -21,6 +21,7 @@ using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Views;
using AndroidX.Core.Content;
using KeePassLib.Serialization;
namespace keepass2android
@@ -55,7 +56,7 @@ namespace keepass2android
filter.AddAction(Intents.DatabaseLocked);
filter.AddAction(Intent.ActionScreenOff);
RegisterReceiver(_intentReceiver, filter);
ContextCompat.RegisterReceiver(this, _intentReceiver, filter, (int)ReceiverFlags.Exported);
}

View File

@@ -18,6 +18,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
using System;
using Android.Content;
using Android.OS;
using AndroidX.Core.Content;
using KeePassLib.Serialization;
namespace keepass2android
@@ -39,7 +40,7 @@ namespace keepass2android
_intentReceiver = new LockCloseActivityBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
RegisterReceiver(_intentReceiver, filter);
ContextCompat.RegisterReceiver(this, _intentReceiver, filter, (int)ReceiverFlags.Exported);
}
protected override void OnResume() {

View File

@@ -43,7 +43,7 @@
</queries>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_notify_locked" android:label="KP2A entry search" android:name="keepass2android.keepass2android_debug.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android_debug.permission.Kp2aChooseAutofill" android:protectionLevel="signature" />
<application
@@ -256,6 +256,7 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" android:maxSdkVersion="22" />
<uses-permission android:name="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" />

View File

@@ -42,7 +42,7 @@
<action android:name="android.intent.action.VIEW" />
</intent>
</queries>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher" android:label="KP2A entry search" android:name="keepass2android.keepass2android.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android.permission.Kp2aChooseAutofill" android:protectionLevel="signature" />
@@ -270,6 +270,7 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" android:maxSdkVersion="22" />
<uses-permission android:name="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing" />

View File

@@ -40,7 +40,7 @@
</intent>
</queries>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher_offline" android:label="KP2A entry search" android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher_offline" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android_nonet.permission.Kp2aChooseAutofill" android:protectionLevel="signature" />
<application

View File

@@ -65,6 +65,7 @@ using Enum = System.Enum;
using Exception = System.Exception;
using String = System.String;
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
using AndroidX.Core.Content;
namespace keepass2android
{
@@ -647,7 +648,7 @@ namespace keepass2android
_intentReceiver = new PasswordActivityBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intent.ActionScreenOff);
RegisterReceiver(_intentReceiver, filter);
ContextCompat.RegisterReceiver(this, _intentReceiver, filter, (int)ReceiverFlags.Exported);
//use FlagSecure to make sure the last (revealed) character of the master password is not visible in recent apps

View File

@@ -34,6 +34,7 @@ using keepass2android;
using KeePassLib;
using KeePassLib.Serialization;
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
using AndroidX.Core.Content;
namespace keepass2android
{
@@ -153,7 +154,7 @@ namespace keepass2android
_intentReceiver = new QuickUnlockBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
RegisterReceiver(_intentReceiver, filter);
ContextCompat.RegisterReceiver(this, _intentReceiver, filter, (int)ReceiverFlags.Exported);
Util.SetNoPersonalizedLearning(FindViewById<EditText>(Resource.Id.QuickUnlock_password));
@@ -503,8 +504,6 @@ namespace keepass2android
}
}
}
}
}

View File

@@ -69,6 +69,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputLayout
@@ -82,7 +83,7 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:inputType="textPassword"
android:hint="password"
android:hint="@string/hint_pass"
android:importantForAccessibility="no"/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
@@ -135,6 +136,7 @@
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:id="@+id/entry_extras_container"
style="@style/EntryEditSingleLine_container">
<ImageView
@@ -173,6 +175,7 @@
</LinearLayout>
<!-- file attachments -->
<LinearLayout
android:orientation="vertical"
android:id="@+id/entry_binaries_container"
style="@style/EntryEditSingleLine_container">
<ImageView
@@ -228,6 +231,7 @@
</LinearLayout>
<!--expires-->
<LinearLayout
android:orientation="vertical"
android:id="@+id/expires_section"
style="@style/EntryEditSingleLine_container">
<ImageView

View File

@@ -26,6 +26,7 @@ using KeePassLib.Keys;
using KeePassLib.Serialization;
using Console = System.Console;
using Object = Java.Lang.Object;
using AndroidX.Core.Content;
namespace keepass2android
{
@@ -343,7 +344,7 @@ namespace keepass2android
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
filter.AddAction(Intent.ActionScreenOff);
RegisterReceiver(_intentReceiver, filter);
ContextCompat.RegisterReceiver(this, _intentReceiver, filter, (int)ReceiverFlags.Exported);
}
}

View File

@@ -1342,7 +1342,7 @@ namespace keepass2android
intentFilter.AddAction(Intents.LockDatabase);
intentFilter.AddAction(Intents.LockDatabaseByTimeout);
intentFilter.AddAction(Intents.CloseDatabase);
Context.RegisterReceiver(broadcastReceiver, intentFilter);
ContextCompat.RegisterReceiver(Context, broadcastReceiver, intentFilter, (int)ReceiverFlags.Exported);
//ZXing.Net.Mobile.Forms.Android.Platform.Init();
}

View File

@@ -71,7 +71,7 @@ namespace keepass2android.services.AutofillBase
static readonly HashSet<string> _trustedBrowsers = new HashSet<string>
{
"org.mozilla.firefox","org.mozilla.firefox_beta","org.mozilla.klar","org.mozilla.focus",
"org.mozilla.fenix","org.mozilla.fenix.nightly","org.mozilla.reference.browser",
"org.mozilla.fenix","org.mozilla.reference.browser",
"com.android.browser","com.android.chrome","com.chrome.beta","com.chrome.dev","com.chrome.canary",
"com.google.android.apps.chrome","com.google.android.apps.chrome_dev",
"com.opera.browser","com.opera.browser.beta","com.opera.mini.native","com.opera.mini.native.beta","com.opera.touch",

View File

@@ -86,6 +86,8 @@ namespace keepass2android.services.AutofillBase
ParseRecursive(autofillView, view, isManualRequest);
}
autofillView.PackageId = autofillView.PackageId ?? _structure.ActivityComponent.PackageName;
return autofillView;
}
@@ -122,7 +124,6 @@ namespace keepass2android.services.AutofillBase
}
autofillView.InputFields.Add(new ViewNodeInputField(viewNode));
var childrenSize = viewNode.ChildCount;
if (childrenSize > 0)
{

View File

@@ -39,6 +39,7 @@ using KeePass.Util.Spr;
using keepass2android;
using KeePassLib.Serialization;
using PluginTOTP;
using AndroidX.Core.Content;
namespace keepass2android
{
@@ -322,7 +323,8 @@ namespace keepass2android
_stopOnLockBroadcastReceiver = new StopOnLockBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
RegisterReceiver(_stopOnLockBroadcastReceiver, filter);
ContextCompat.RegisterReceiver(this, _stopOnLockBroadcastReceiver, filter, (int)ReceiverFlags.Exported);
}
if ((intent.Action == Intents.ShowNotification) || (intent.Action == Intents.UpdateKeyboard))
@@ -529,7 +531,7 @@ namespace keepass2android
_notificationDeletedBroadcastReceiver = new NotificationDeletedBroadcastReceiver(this);
IntentFilter deletefilter = new IntentFilter();
deletefilter.AddAction(ActionNotificationCancelled);
RegisterReceiver(_notificationDeletedBroadcastReceiver, deletefilter);
ContextCompat.RegisterReceiver(this, _notificationDeletedBroadcastReceiver, deletefilter, (int)ReceiverFlags.Exported);
}
}

View File

@@ -18,10 +18,12 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.OS;
using Android.Preferences;
using AndroidX.Core.App;
using AndroidX.Core.Content;
using keepass2android;
using KeePassLib.Utility;
@@ -37,7 +39,9 @@ namespace keepass2android
/// used by the user. This ensures the database is kept in memory (until Android kills it due to low memory).
/// It is important to also have a foreground service also for the "unlocked" state because it's really
/// irritating if the db is closed while switching between apps.
[Service]
[Service(ForegroundServiceType = ForegroundService.TypeSpecialUse )]
[MetaData("android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE", Value = " This service is running as foreground service to keep the app alive even when it's not currently used by the user. This ensures the database is kept in memory (until Android kills it due to low memory). It is important to also have a foreground service also for the \"unlocked\" state because it's really irritating if the db is closed while switching between apps.")]
public class OngoingNotificationsService : Service
{
protected override void AttachBaseContext(Context baseContext)
@@ -57,7 +61,7 @@ namespace keepass2android
_screenOffReceiver = new ScreenOffReceiver();
IntentFilter filter = new IntentFilter();
filter.AddAction(Intent.ActionScreenOff);
RegisterReceiver(_screenOffReceiver, filter);
ContextCompat.RegisterReceiver(this, _screenOffReceiver, filter, (int)ReceiverFlags.Exported);
}