Merge branch 'master' into upgrade-fluentftp
This commit is contained in:
@@ -292,8 +292,20 @@ namespace keepass2android.Io
|
||||
{
|
||||
using (var client = GetClient(ioc))
|
||||
{
|
||||
/*
|
||||
* For some reason GetListing(path) does not always return the contents of the directory.
|
||||
* However, calling SetWorkingDirectory(path) followed by GetListing(null, options) to
|
||||
* list the contents of the working directory does consistently work.
|
||||
*
|
||||
* Similar behavior was confirmed using ncftp client. I suspect this is a strange
|
||||
* bug/nuance in the server's implementation of the LIST command?
|
||||
*
|
||||
* [bug #2423]
|
||||
*/
|
||||
client.SetWorkingDirectory(IocToLocalPath(ioc));
|
||||
|
||||
List<FileDescription> files = new List<FileDescription>();
|
||||
foreach (FtpListItem item in client.GetListing(IocToLocalPath(ioc),
|
||||
foreach (FtpListItem item in client.GetListing(null,
|
||||
FtpListOption.SizeModify | FtpListOption.AllFiles))
|
||||
{
|
||||
switch (item.Type)
|
||||
|
||||
@@ -48,6 +48,8 @@ using KeePassLib.Serialization;
|
||||
using PluginTOTP;
|
||||
using File = Java.IO.File;
|
||||
using Uri = Android.Net.Uri;
|
||||
using keepass2android.fileselect;
|
||||
using Boolean = Java.Lang.Boolean;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
@@ -554,21 +556,90 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal void StartNotificationsService(bool activateKeyboard)
|
||||
{
|
||||
Intent showNotIntent = new Intent(this, typeof (CopyToClipboardService));
|
||||
showNotIntent.SetAction(Intents.ShowNotification);
|
||||
showNotIntent.PutExtra(KeyEntry, new ElementAndDatabaseId(App.Kp2a.CurrentDb, Entry).FullId);
|
||||
AppTask.PopulatePasswordAccessServiceIntent(showNotIntent);
|
||||
showNotIntent.PutExtra(KeyActivateKeyboard, activateKeyboard);
|
||||
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
|
||||
{
|
||||
if (permissions.Length == 1 && permissions.First() == Android.Manifest.Permission.PostNotifications &&
|
||||
grantResults.First() == Permission.Granted)
|
||||
{
|
||||
StartNotificationsServiceAfterPermissionsCheck(requestCode == 1 /*requestCode is used to transfer this flag*/);
|
||||
}
|
||||
|
||||
StartService(showNotIntent);
|
||||
}
|
||||
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
internal void StartNotificationsService(bool activateKeyboard)
|
||||
{
|
||||
if (PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(
|
||||
GetString(Resource.String.CopyToClipboardNotification_key),
|
||||
Resources.GetBoolean(Resource.Boolean.CopyToClipboardNotification_default)) == false
|
||||
&& PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(
|
||||
GetString(Resource.String.UseKp2aKeyboard_key),
|
||||
Resources.GetBoolean(Resource.Boolean.UseKp2aKeyboard_default)) == false)
|
||||
{
|
||||
//notifications are disabled
|
||||
return;
|
||||
}
|
||||
|
||||
if ((int)Build.VERSION.SdkInt < 33 || CheckSelfPermission(Android.Manifest.Permission.PostNotifications) ==
|
||||
Permission.Granted)
|
||||
{
|
||||
StartNotificationsServiceAfterPermissionsCheck(activateKeyboard);
|
||||
return;
|
||||
}
|
||||
|
||||
//user has not yet granted Android 13's POST_NOTIFICATONS permission for the app.
|
||||
|
||||
//check if we should ask them to grant:
|
||||
if (!ShouldShowRequestPermissionRationale(Android.Manifest.Permission.PostNotifications) //this menthod returns false if we haven't asked yet or if the user has denied permission too often
|
||||
&& PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean("RequestedPostNotificationsPermission", false))//use a preference to tell the difference between "haven't asked yet" and "have asked too often"
|
||||
{
|
||||
//user has denied permission before. Do not show the dialog. User must give permission in the Android App settings.
|
||||
return;
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.SetTitle(Resource.String.post_notifications_dialog_title)
|
||||
.SetMessage(Resource.String.post_notifications_dialog_message)
|
||||
.SetNegativeButton(Resource.String.post_notifications_dialog_disable, (sender, args) =>
|
||||
{
|
||||
//disable this dialog for the future by disabling the notification preferences
|
||||
var edit= PreferenceManager.GetDefaultSharedPreferences(this).Edit();
|
||||
edit.PutBoolean(GetString(Resource.String.CopyToClipboardNotification_key), false);
|
||||
edit.PutBoolean(GetString(Resource.String.UseKp2aKeyboard_key), false);
|
||||
edit.Commit();
|
||||
})
|
||||
.SetPositiveButton(Resource.String.post_notifications_dialog_allow, (sender, args) =>
|
||||
{
|
||||
|
||||
//remember that we did ask for permission at least once:
|
||||
var edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
|
||||
edit.PutBoolean("RequestedPostNotificationsPermission", true);
|
||||
edit.Commit();
|
||||
|
||||
//request permission. user must grant, we'll show notifications in the OnRequestPermissionResults() callback
|
||||
Android.Support.V4.App.ActivityCompat.RequestPermissions(this, new[] { Android.Manifest.Permission.PostNotifications }, activateKeyboard ? 1 : 0 /*use requestCode to transfer the flag*/);
|
||||
|
||||
|
||||
private String getDateTime(DateTime dt)
|
||||
})
|
||||
.SetNeutralButton(Resource.String.post_notifications_dialog_notnow, (sender, args) => { })
|
||||
.Show();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void StartNotificationsServiceAfterPermissionsCheck(bool activateKeyboard)
|
||||
{
|
||||
Intent showNotIntent = new Intent(this, typeof(CopyToClipboardService));
|
||||
showNotIntent.SetAction(Intents.ShowNotification);
|
||||
showNotIntent.PutExtra(KeyEntry, new ElementAndDatabaseId(App.Kp2a.CurrentDb, Entry).FullId);
|
||||
AppTask.PopulatePasswordAccessServiceIntent(showNotIntent);
|
||||
showNotIntent.PutExtra(KeyActivateKeyboard, activateKeyboard);
|
||||
|
||||
StartService(showNotIntent);
|
||||
}
|
||||
|
||||
|
||||
private String getDateTime(DateTime dt)
|
||||
{
|
||||
return dt.ToLocalTime().ToString("g", CultureInfo.CurrentUICulture);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
@@ -57,8 +58,9 @@ namespace keepass2android
|
||||
{ Resource.Id.child_db_infotext, 13 },
|
||||
{ Resource.Id.fingerprint_infotext, 12 },
|
||||
{ Resource.Id.autofill_infotext, 11 },
|
||||
{ Resource.Id.notification_info_android8_infotext, 10 },
|
||||
{ Resource.Id.infotext, 9 },
|
||||
{ Resource.Id.notification_permission_infotext, 10 },
|
||||
{ Resource.Id.notification_info_android8_infotext, 9 },
|
||||
{ Resource.Id.infotext, 8 },
|
||||
{ Resource.Id.select_other_entry, 20},
|
||||
{ Resource.Id.add_url_entry, 20},
|
||||
};
|
||||
@@ -273,6 +275,7 @@ namespace keepass2android
|
||||
UpdateFingerprintInfo();
|
||||
UpdateAutofillInfo();
|
||||
UpdateAndroid8NotificationInfo();
|
||||
UpdatePostNotificationsPermissionInfo();
|
||||
UpdateInfotexts();
|
||||
|
||||
RefreshIfDirty();
|
||||
@@ -280,6 +283,7 @@ namespace keepass2android
|
||||
SetSearchItemVisibility();
|
||||
}
|
||||
|
||||
|
||||
private void UpdateInfotexts()
|
||||
{
|
||||
|
||||
@@ -385,6 +389,31 @@ namespace keepass2android
|
||||
hasCalledOtherActivity = false;
|
||||
}
|
||||
|
||||
|
||||
private void UpdatePostNotificationsPermissionInfo(bool hideForever=false)
|
||||
{
|
||||
const string prefsKey = "DidShowNotificationPermissionInfo";
|
||||
|
||||
bool canShowNotificationInfo = ((int)Build.VERSION.SdkInt >= 33)
|
||||
&& (!_prefs.GetBoolean(prefsKey, false)
|
||||
&& (CheckSelfPermission(Android.Manifest.Permission.PostNotifications) !=
|
||||
Permission.Granted)
|
||||
&& (ShouldShowRequestPermissionRationale(Android.Manifest.Permission.PostNotifications) //this menthod returns false if we haven't asked yet or if the user has denied permission too often
|
||||
|| !PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean("RequestedPostNotificationsPermission", false))//use a preference to tell the difference between "haven't asked yet" and "have asked too often"
|
||||
);
|
||||
if ((canShowNotificationInfo) && hideForever)
|
||||
{
|
||||
_prefs.Edit().PutBoolean(prefsKey, true).Commit();
|
||||
canShowNotificationInfo = false;
|
||||
}
|
||||
if (canShowNotificationInfo)
|
||||
{
|
||||
RegisterInfoTextDisplay("NotificationPermissionInfo"); //this ensures that we don't show the general info texts too soon
|
||||
}
|
||||
UpdateBottomBarElementVisibility(Resource.Id.notification_permission_infotext, canShowNotificationInfo);
|
||||
|
||||
}
|
||||
|
||||
private void UpdateAndroid8NotificationInfo(bool hideForever = false)
|
||||
{
|
||||
const string prefsKey = "DidShowAndroid8NotificationInfo";
|
||||
@@ -606,6 +635,25 @@ namespace keepass2android
|
||||
|
||||
}
|
||||
|
||||
if (FindViewById(Resource.Id.post_notification_button_allow) != null)
|
||||
{
|
||||
FindViewById(Resource.Id.post_notification_button_allow).Click += (sender, args) =>
|
||||
{
|
||||
//remember that we did ask for permission at least once:
|
||||
var edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
|
||||
edit.PutBoolean("RequestedPostNotificationsPermission", true);
|
||||
edit.Commit();
|
||||
|
||||
Android.Support.V4.App.ActivityCompat.RequestPermissions(this, new[] { Android.Manifest.Permission.PostNotifications }, 0);
|
||||
UpdatePostNotificationsPermissionInfo(true);
|
||||
};
|
||||
FindViewById(Resource.Id.post_notification_button_dont_show_again).Click += (sender, args) =>
|
||||
{
|
||||
UpdatePostNotificationsPermissionInfo(true);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</queries>
|
||||
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||
<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
|
||||
@@ -258,6 +258,7 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
|
||||
<uses-permission android:name="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<!-- Samsung Pass permission -->
|
||||
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
|
||||
<!-- READ_PHONE_STATE seems to come from some library or so, not clear where. We don't want to have it, remove it: -->
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
</intent>
|
||||
</queries>
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
||||
<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,10 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
|
||||
<uses-permission android:name="keepass2android.keepass2android.permission.KP2aInternalSearch" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||
|
||||
<!-- Samsung Pass permission -->
|
||||
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="31" />
|
||||
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="33" />
|
||||
<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
|
||||
@@ -243,6 +243,9 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||
|
||||
<uses-permission android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalFileBrowsing" />
|
||||
<uses-permission android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalSearch" />
|
||||
|
||||
@@ -245,6 +245,50 @@
|
||||
style="@style/BottomBarButton" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_permission_infotext"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
<TextView android:id="@+id/infotext" android:text="@string/PostNotificationsPermissionInfo_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:layout_margin="6dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/notification_permissions_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<Button
|
||||
android:id="@+id/post_notification_button_allow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:paddingTop="4dp"
|
||||
android:text="@string/post_notifications_dialog_allow"
|
||||
style="@style/BottomBarButton" />
|
||||
<Button
|
||||
android:id="@+id/post_notification_button_dont_show_again"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:paddingTop="4dp"
|
||||
android:text="@string/dont_show_again"
|
||||
style="@style/BottomBarButton" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/infotext"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -484,6 +484,10 @@
|
||||
|
||||
<string name="IconVisibilityInfo_Android8_text">Android 8 has introduced new behavior for notifications. If you want to hide the icon for Keepass2Android\'s notifications, please configure this through the system settings. Set the importance of the notification category to Minimum.</string>
|
||||
<string name="IconVisibilityInfo_Android8_btnSettings">Open settings</string>
|
||||
|
||||
<string name="PostNotificationsPermissionInfo_text">Keepass2Android can display a system notification while your database is not locked. For this to work, please grant permission.</string>
|
||||
|
||||
|
||||
<string name="DontCare">I don\'t care</string>
|
||||
|
||||
<string name="DocumentAccessRevoked">The file is no longer accessible to Keepass2Android. Either it was removed or the access permissions have been revoked. Please use re-open the file, e.g. using Change database.</string>
|
||||
@@ -1369,6 +1373,12 @@ Initial public release
|
||||
|
||||
<string name="enable_fingerprint_hint">Keepass2Android has detected biometric hardware. Do you want to enable Biometric Unlock for this database?</string>
|
||||
|
||||
<string name="post_notifications_dialog_title">Allow notifications</string>
|
||||
<string name="post_notifications_dialog_message">Keepass2Android can show notifications with buttons to copy values like passwords and TOTPs to clipboard, or to bring up the built-in keyboard. This is useful to transfer values into other apps without switching to Keepass2Android repeatedly. Do you want to enable such notifications?</string>
|
||||
<string name="post_notifications_dialog_allow">Allow notifications</string>
|
||||
<string name="post_notifications_dialog_disable">Disable this feature</string>
|
||||
<string name="post_notifications_dialog_notnow">Not now</string>
|
||||
|
||||
<string name="understand">I understand</string>
|
||||
<string name="dont_show_again">Do not show again</string>
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@ namespace keepass2android
|
||||
}
|
||||
else
|
||||
{
|
||||
//Anrdoid 8 requires that we call StartForeground() shortly after starting the service with StartForegroundService.
|
||||
//Android 8 requires that we call StartForeground() shortly after starting the service with StartForegroundService.
|
||||
//This is not possible when we're closing the service. In this case we don't use the StopSelf in the OngoingNotificationsService.OnStartCommand() anymore but directly stop the service.
|
||||
|
||||
OngoingNotificationsService.CancelNotifications(ctx); //The docs are not 100% clear if OnDestroy() will be called immediately. So make sure the notifications are up to date.
|
||||
|
||||
Reference in New Issue
Block a user