update to targetSdkVersion=33 again, this time with handling of notification permissions (https://developer.android.com/develop/ui/views/notifications/notification-permission)

For now, this is only implemented for entry activity, unlocked/QuickUnlock notifications not tested/implemented yet.
This commit is contained in:
Philipp Crocoll
2023-10-30 17:33:39 +01:00
parent 7c2500af63
commit 150bd336d8
6 changed files with 95 additions and 15 deletions

View File

@@ -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);
}

View File

@@ -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: -->

View File

@@ -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,7 @@ 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" />

View File

@@ -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,7 @@ 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" />

View File

@@ -1369,6 +1369,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>

View File

@@ -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.