What changed, and why it matters
This commit fixes a cosmetic Android notification icon problem. It swaps out a third-party library ('plyer') for a direct Android system call so Electrum's notifications show the correct icon instead of a blank circle. There is no direct security bug being patched, but the change touches notification code and removes one external dependency, which slightly reduces attack surface.
Treat as a routine UI/maintenance change. Reviewers may optionally verify the new notification path does not expose sensitive wallet data in notification text and that the removed plyer recipe is no longer referenced elsewhere in the build.
Security signals we found
Dependency removal reduces supply-chain/dependency attack surface
New code uses Android JNI notification APIs and PendingIntent with FLAG_IMMUTABLE
No input validation or sanitization changes visible for notification title/message
No explicit security issue disclosed in commit message or diff
Evidence from the diff
The patch removes the ‘plyer’ dependency from the Android build and replaces it with a pyjnius-based implementation in electrum/gui/qml/qeapp.py. The new _notify_android() method builds an Android Notification using the app icon resource, creates a notification channel, sets a PendingIntent, and posts the notification. The commit message frames this as a UI fix for the notification icon. No vulnerability, CVE, or security disclosure is referenced in the commit or supplied materials.
Changed components
electrum/gui/qml/qeapp.pycontrib/android/buildozer_qml.speccontrib/android/p4a_recipes/plyer/__init__.pyInspect captured patch +41 / −15
### contrib/android/buildozer_qml.spec
@@ -75,7 +75,6 @@ requirements =
python3,
android,
openssl,
- plyer,
libffi,
libsecp256k1,
pycryptodomex,
@@ -176,6 +175,7 @@ android.add_activities = org.electrum.biometry.BiometricActivity
# 3) A directory, here 'legal_resources' must contain one or more directories,
# each of a resource kind: drawable, xml, etc...
# android.add_resources = legal_resources
+android.add_resources = electrum/gui/icons/electrum_light_icon.png:drawable
# (str) python-for-android branch to use, if not master, useful to try
# not yet merged features.
### contrib/android/p4a_recipes/plyer/__init__.py
@@ -1,14 +0,0 @@
-from pythonforandroid.recipe import PythonRecipe
-
-
-assert PythonRecipe.depends == ['python3']
-assert PythonRecipe.python_depends == []
-
-
-class PlyerRecipePinned(PythonRecipe):
- version = "5262087c85b2c82c69e702fe944069f1d8465fdf"
- url = "git+https://github.com/SomberNight/plyer"
- depends = ["setuptools"]
-
-
-recipe = PlyerRecipePinned()
### electrum/gui/qml/qeapp.py
@@ -172,6 +172,9 @@ def doNotify(self, wallet_name, message):
os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "icons", "electrum.png",
)
try:
+ if self.isAndroid():
+ self._notify_android('Electrum', message, large_icon=icon)
+ return
# TODO: lazy load not in UI thread please
global notification
if not notification:
@@ -182,6 +185,43 @@ def doNotify(self, wallet_name, message):
except Exception as e:
self.logger.error(repr(e))
+ def _notify_android(self, title: str, message: str, *, large_icon: str):
+ # Not using plyer here, as it uses the launcher icon as small icon. Android only
+ # uses the alpha channel of the small icon, so our opaque adaptive launcher icon
+ # is shown as a blank disc.
+ jContext = autoclass('android.content.Context')
+ jNotificationManager = autoclass('android.app.NotificationManager')
+ jNotificationChannel = autoclass('android.app.NotificationChannel')
+ jNotificationBuilder = autoclass('android.app.Notification$Builder')
+ jPendingIntent = autoclass('android.app.PendingIntent')
+ jBitmapFactory = autoclass('android.graphics.BitmapFactory')
+
+ package_name = jpythonActivity.getPackageName()
+ channel_id = package_name # same channel id as plyer used, keeps user's channel settings
+ nm = cast(jNotificationManager, jpythonActivity.getSystemService(jContext.NOTIFICATION_SERVICE))
+ nm.createNotificationChannel(
+ jNotificationChannel(channel_id, jString(title), jNotificationManager.IMPORTANCE_DEFAULT))
+
+ # electrum_light_icon.png is added to res/drawable by android.add_resources in buildozer_qml.spec
+ small_icon = jpythonActivity.getResources().getIdentifier('electrum_light_icon', 'drawable', package_name)
+ if not small_icon: # not packaged as expected, fall back to app icon
+ self.logger.warning('Notification icon not found')
+ small_icon = jpythonActivity.getApplicationInfo().icon
+
+ builder = jNotificationBuilder(jpythonActivity, channel_id)
+ builder.setSmallIcon(small_icon)
+ builder.setContentTitle(jString(title))
+ builder.setContentText(jString(message))
+ bitmap = jBitmapFactory.decodeFile(large_icon)
+ if bitmap is not None:
+ builder.setLargeIcon(bitmap)
+ launch_intent = jpythonActivity.getPackageManager().getLaunchIntentForPackage(package_name)
+ if launch_intent is not None:
+ builder.setContentIntent(
+ jPendingIntent.getActivity(jpythonActivity, 0, launch_intent, jPendingIntent.FLAG_IMMUTABLE))
+ builder.setAutoCancel(True)
+ nm.notify(0, builder.build())
+
def bindIntent(self):
if not self.isAndroid():
returnWhy this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.