diff --git a/android_notify/an_utils.py b/android_notify/an_utils.py index a15edb0..77aa988 100644 --- a/android_notify/an_utils.py +++ b/android_notify/an_utils.py @@ -6,7 +6,7 @@ from .config import ( get_python_activity_context, app_storage_path,ON_ANDROID, BitmapFactory, BuildVersion, Bundle, - NotificationManagerClass,AndroidNotification + NotificationManagerClass,AndroidNotification,Intent, Settings, Uri, String, Manifest ) if ON_ANDROID: @@ -143,7 +143,6 @@ def get_package_path(): return os.path.dirname(os.path.abspath(__file__)) def get_bitmap_from_path(img_full_path): - Uri = autoclass('android.net.Uri') uri = Uri.parse(f"file://{img_full_path}") return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri)) @@ -156,3 +155,35 @@ def icon_finder(icon_name): # Fallback if pkg_resources not available package_dir = get_package_path() return os.path.join(package_dir, "fallback-icons", icon_name) + +def can_show_permission_request_popup(): + """ + Check if we can show permission request popup for POST_NOTIFICATIONS + :return: bool + """ + if not ON_ANDROID or BuildVersion.SDK_INT < 33: + return False + + return context.shouldShowRequestPermissionRationale(Manifest.POST_NOTIFICATIONS) + +def open_settings_screen(): + intent = Intent() + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + package_name = String(context.getPackageName()) # String() is very important else fails silently with a toast + # saying "The app wasn't found in the list of installed apps" - Xiaomi or "unable to find application to perform this action" - Samsung and Techno + + if BuildVersion.SDK_INT >= 26: # Android 8.0 - android.os.Build.VERSION_CODES.O + intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS) + intent.putExtra(Settings.EXTRA_APP_PACKAGE, package_name) + elif BuildVersion.SDK_INT >= 22: # Android 5.0 - Build.VERSION_CODES.LOLLIPOP + intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS") + intent.putExtra("app_package", package_name) + intent.putExtra("app_uid", context.getApplicationInfo().uid) + else: # Last Retort is to open App Settings Screen + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intent.addCategory(Intent.CATEGORY_DEFAULT) + intent.setData(Uri.parse("package:" + package_name)) + + context.startActivity(intent) + + # https://stackoverflow.com/a/45192258/19961621 diff --git a/android_notify/config.py b/android_notify/config.py index ab8fdd6..8428834 100644 --- a/android_notify/config.py +++ b/android_notify/config.py @@ -44,6 +44,9 @@ def get_activity_class_name(): NotificationChannel = autoclass('android.app.NotificationChannel') RemoteViews = autoclass('android.widget.RemoteViews') AndroidNotification = autoclass("android.app.Notification") + Settings = autoclass("android.provider.Settings") + Uri = autoclass("android.net.Uri") + Manifest = autoclass('android.Manifest$permission') ON_ANDROID = bool(RemoteViews) except Exception as e: diff --git a/android_notify/core.py b/android_notify/core.py index aee8663..3300e47 100644 --- a/android_notify/core.py +++ b/android_notify/core.py @@ -1,7 +1,7 @@ """ Non-Advanced Stuff """ import random import os, traceback -from .config import get_python_activity +from .config import get_python_activity, Manifest ON_ANDROID = False def on_flet_app(): @@ -37,6 +37,8 @@ def on_flet_app(): traceback.print_exc() print("Error importing notification styles") +from .an_utils import can_show_permission_request_popup, open_settings_screen + def get_app_root_path(): path = '' @@ -55,18 +57,25 @@ def asks_permission_if_needed(no_androidx=False): """ Ask for permission to send notifications if needed. """ + if BuildVersion.SDK_INT < 33 or not ON_ANDROID: + return True + + if not can_show_permission_request_popup(): + print("""android_notify- Permission to send notifications has been denied permanently. Please enable it from settings. + This happens when the user denies permission twice from the popup.""") + open_settings_screen() + return + + if on_flet_app() or no_androidx: Activity = autoclass("android.app.Activity") - Manifest = autoclass("android.Manifest$permission") PackageManager = autoclass("android.content.pm.PackageManager") - VERSION_CODES = autoclass('android.os.Build$VERSION_CODES') - - if BuildVersion.SDK_INT >= 33: - permission = Manifest.POST_NOTIFICATIONS - granted = context.checkSelfPermission(permission) + + permission = Manifest.POST_NOTIFICATIONS + granted = context.checkSelfPermission(permission) - if granted != PackageManager.PERMISSION_GRANTED: - context.requestPermissions([permission], 101) + if granted != PackageManager.PERMISSION_GRANTED: + context.requestPermissions([permission], 101) else: # android package is from p4a which is for kivy try: from android.permissions import request_permissions, Permission,check_permission # type: ignore diff --git a/android_notify/sword.py b/android_notify/sword.py index f95096e..d03b702 100644 --- a/android_notify/sword.py +++ b/android_notify/sword.py @@ -6,7 +6,8 @@ from .an_types import Importance from .an_utils import can_accept_arguments, get_python_activity_context, \ get_android_importance, generate_channel_id, get_img_from_path, setLayoutText, \ - get_bitmap_from_url, add_data_to_intent, get_sound_uri, icon_finder, get_bitmap_from_path + get_bitmap_from_url, add_data_to_intent, get_sound_uri, icon_finder, get_bitmap_from_path, can_show_permission_request_popup, open_settings_screen + from .config import from_service_file, get_python_activity,get_notification_manager,ON_ANDROID,on_flet_app from .config import (Bundle, String, BuildVersion, @@ -18,7 +19,7 @@ from .config import (AndroidNotification, NotificationCompatBuilder, NotificationCompatBigTextStyle,NotificationCompatBigPictureStyle, NotificationCompatInboxStyle, - Color + Color, Manifest ) from .styles import NotificationStyles from .base import BaseNotification @@ -1081,13 +1082,29 @@ def asks_permission(cls,callback=None): Ask for permission to send notifications if needed. Passes True to callback if access granted """ - if cls.__requesting_permission or not ON_ANDROID: + if cls.__requesting_permission: return True - + if BuildVersion.SDK_INT < 33: # Android 12 below print("android_notify- On android 12 or less don't need permission") - if callback: - callback(True) + + if not ON_ANDROID or BuildVersion.SDK_INT < 33: # Android 12 below: + try: + if callback: + if can_accept_arguments(callback, True): + callback(True) + else: + callback() + except Exception as request_permission_error: + print('Exception: ',request_permission_error) + print('Permission response callback error: ',traceback.format_exc()) + + return + + if not can_show_permission_request_popup(): + print("""android_notify- Permission to send notifications has been denied permanently. Please enable it from settings. + This happens when the user denies permission twice from the popup.""") + open_settings_screen() return def on_permissions_result(permissions, grants):