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:
		| @@ -48,6 +48,8 @@ using KeePassLib.Serialization; | |||||||
| using PluginTOTP; | using PluginTOTP; | ||||||
| using File = Java.IO.File; | using File = Java.IO.File; | ||||||
| using Uri = Android.Net.Uri; | using Uri = Android.Net.Uri; | ||||||
|  | using keepass2android.fileselect; | ||||||
|  | using Boolean = Java.Lang.Boolean; | ||||||
|  |  | ||||||
| namespace keepass2android | namespace keepass2android | ||||||
| { | { | ||||||
| @@ -554,21 +556,90 @@ namespace keepass2android | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		 |  | ||||||
|  |  | ||||||
| 		internal void StartNotificationsService(bool activateKeyboard) |         public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) | ||||||
| 		{ |         { | ||||||
| 			Intent showNotIntent = new Intent(this, typeof (CopyToClipboardService)); |             if (permissions.Length == 1 && permissions.First() == Android.Manifest.Permission.PostNotifications && | ||||||
| 			showNotIntent.SetAction(Intents.ShowNotification); |                 grantResults.First() == Permission.Granted) | ||||||
| 			showNotIntent.PutExtra(KeyEntry, new ElementAndDatabaseId(App.Kp2a.CurrentDb, Entry).FullId); |             { | ||||||
| 			AppTask.PopulatePasswordAccessServiceIntent(showNotIntent); |                 StartNotificationsServiceAfterPermissionsCheck(requestCode == 1 /*requestCode is used to transfer this flag*/); | ||||||
| 			showNotIntent.PutExtra(KeyActivateKeyboard, activateKeyboard); |             } | ||||||
|  |  | ||||||
| 			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); | 			return dt.ToLocalTime().ToString("g", CultureInfo.CurrentUICulture); | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -43,7 +43,7 @@ | |||||||
|   </queries>					 |   </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_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" /> | <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  | 	<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="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" /> | ||||||
| 	<uses-permission android:name="android.permission.USE_FINGERPRINT" /> | 	<uses-permission android:name="android.permission.USE_FINGERPRINT" /> | ||||||
|   <uses-permission android:name="android.permission.USE_BIOMETRIC" /> |   <uses-permission android:name="android.permission.USE_BIOMETRIC" /> | ||||||
|  |   <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> | ||||||
| 	<!-- Samsung Pass permission --> | 	<!-- Samsung Pass permission --> | ||||||
| 	<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" /> | 	<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: --> | <!-- 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" /> |         <action android:name="android.intent.action.VIEW" /> | ||||||
| 	  </intent> | 	  </intent> | ||||||
|   </queries>			 |   </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_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" /> |   <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="keepass2android.keepass2android.permission.KP2aInternalSearch" /> | ||||||
| 	<uses-permission android:name="android.permission.USE_FINGERPRINT" /> | 	<uses-permission android:name="android.permission.USE_FINGERPRINT" /> | ||||||
|   <uses-permission android:name="android.permission.USE_BIOMETRIC" /> |   <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-feature android:name="android.hardware.camera" android:required="false" /> | ||||||
|    |    | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ | |||||||
| 	  </intent> | 	  </intent> | ||||||
|   </queries>			 |   </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_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" /> |   <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  | 	<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.WRITE_EXTERNAL_STORAGE" /> | ||||||
| 	<uses-permission android:name="android.permission.USE_FINGERPRINT" /> | 	<uses-permission android:name="android.permission.USE_FINGERPRINT" /> | ||||||
|   <uses-permission android:name="android.permission.USE_BIOMETRIC" /> |   <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-feature android:name="android.hardware.camera" android:required="false" /> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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="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="understand">I understand</string> | ||||||
|   <string name="dont_show_again">Do not show again</string> |   <string name="dont_show_again">Do not show again</string> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -315,7 +315,7 @@ namespace keepass2android | |||||||
|             } |             } | ||||||
| 		    else | 		    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. |                 //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. |                 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
	 Philipp Crocoll
					Philipp Crocoll