Prepare public project for distribution
This commit is contained in:
@@ -2,6 +2,9 @@
|
||||
/project.md
|
||||
/local/
|
||||
/local.properties
|
||||
*.jks
|
||||
*.keystore
|
||||
keystore.properties
|
||||
|
||||
# Gradle and Android build output
|
||||
/.gradle/
|
||||
|
||||
@@ -1,61 +1,43 @@
|
||||
# Notification Log
|
||||
|
||||
Notification Log is a private, non-root Android notification listener for API
|
||||
34–36. It keeps an encrypted local history of notification appearances, edits,
|
||||
and removal reasons, including a distinction between an app cancelling its own
|
||||
notification and a user dismissing it.
|
||||
Notification Log keeps a private history of the notifications that appear on
|
||||
your Android device. It works as a standard Android notification listener; it
|
||||
does not need root or an Xposed module.
|
||||
|
||||
## Privacy and capture limits
|
||||
## What it records
|
||||
|
||||
The event log and copied image files are encrypted at rest with an Android
|
||||
Keystore-backed AES-GCM key. Export is deliberately unencrypted only after the
|
||||
user acknowledges a warning and chooses a destination through Android's Storage
|
||||
Access Framework.
|
||||
- Notifications appearing, changing, and disappearing.
|
||||
- Whether a notification was dismissed by you or cancelled by its app.
|
||||
- Available text, messaging-style content, and readable notification images.
|
||||
- Common removal reasons such as timeouts and channel changes.
|
||||
|
||||
Android exposes standard notification extras such as title, text, big text,
|
||||
inbox lines, messaging-style messages, and readable big-picture content. This
|
||||
app copies a readable bitmap at post time when Android provides one. It cannot
|
||||
reconstruct opaque custom `RemoteViews`, OTP content Android redacts, or image
|
||||
URIs/Icons which Android does not allow it to load. Such images remain marked
|
||||
as `[image]` without causing the text event to be lost.
|
||||
You can choose which event types to record, set app-specific allow/block lists,
|
||||
and suppress routine progress or timer updates. The log view can be tailored,
|
||||
expanded for long entries, and exported as CSV, aligned text, or an HTML ZIP.
|
||||
|
||||
The app declares `QUERY_ALL_PACKAGES` solely to implement the settings pages'
|
||||
“all installed apps” list. A Play-distributed build must meet Google Play's
|
||||
restricted package-visibility policy and provide the required declaration; a
|
||||
future distribution variant may need a narrower app-selection flow.
|
||||
## Privacy
|
||||
|
||||
## Background behavior
|
||||
Logs and retained notification images are encrypted on the device with an
|
||||
Android Keystore key. Exporting is an explicit action and produces an
|
||||
unencrypted file at the location you choose. Android may withhold sensitive or
|
||||
custom notification content; this app records only what Android makes available
|
||||
to a notification listener.
|
||||
|
||||
Android owns the notification-listener connection; this app has no polling,
|
||||
scheduled job, alarm, or boot receiver. When one or more logging types are
|
||||
enabled and notification access has been granted, Android binds the listener
|
||||
again after device boot. When every logging type is disabled, the app disables
|
||||
that listener component: it has no background service and does not start at
|
||||
boot. Re-enabling any logging type re-enables and asks Android to rebind it.
|
||||
When all logging types are disabled, the notification listener is disabled too:
|
||||
the app has no polling, scheduled job, or background service to run.
|
||||
|
||||
## Notification update policy
|
||||
## Requirements
|
||||
|
||||
Each platform notification key is tracked independently. Group summaries are
|
||||
logged as their own platform notifications; the app does not invent extra group
|
||||
events. Ongoing notifications are recorded when Android posts, updates, or
|
||||
removes them, but are never treated as user-dismissible. Identical reposts are
|
||||
ignored. With the default **Ignore routine updates** setting, standard
|
||||
progress/chronometer notifications do not create edit rows; non-routine content
|
||||
changes do. Disabling that setting records the updates as edits.
|
||||
Notification Log supports Android 14 through Android 16 (API levels 34–36).
|
||||
After installation, grant **Notification access** when prompted. The optional
|
||||
app lock uses your device's biometric or credential prompt.
|
||||
|
||||
## Development checks
|
||||
## Development
|
||||
|
||||
Build a debug APK with:
|
||||
|
||||
```bash
|
||||
./gradlew test assembleDebug
|
||||
./scripts/run-aosp-api36-notification-smoke.sh
|
||||
./gradlew assembleDebug
|
||||
```
|
||||
|
||||
The AOSP API 36 smoke scenario starts from a clean emulator snapshot, grants
|
||||
listener access, drives all helper notification variants, verifies encrypted
|
||||
event and image persistence, and verifies that the listener records another
|
||||
event after an emulator reboot. It deliberately leaves user swipe dismissal,
|
||||
biometric lock behavior, and OEM/foldable rendering to device testing.
|
||||
|
||||
The local Android test lab has been checked on AOSP API 34/35/36, Google APIs
|
||||
API 35/36, and LineageOS API 35/36 using clean LSPosed snapshots. Root and
|
||||
Xposed are not used by this application.
|
||||
This project has been developed with substantial assistance from AI tools.
|
||||
|
||||
+49
-2
@@ -1,3 +1,5 @@
|
||||
import java.io.File
|
||||
import java.util.Properties
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
@@ -5,6 +7,32 @@ plugins {
|
||||
alias(libs.plugins.kotlin.android)
|
||||
}
|
||||
|
||||
val releaseSigningProperties = Properties()
|
||||
val releaseSigningPropertiesFile = File(
|
||||
System.getProperty("user.home"),
|
||||
".config/android/release-signing.properties",
|
||||
)
|
||||
if (releaseSigningPropertiesFile.isFile) {
|
||||
releaseSigningPropertiesFile.inputStream().use(releaseSigningProperties::load)
|
||||
}
|
||||
|
||||
val releaseKeystorePath = releaseSigningProperties.getProperty("storeFile")
|
||||
val releaseKeyAlias = releaseSigningProperties.getProperty("keyAlias")
|
||||
val releaseStorePassword = providers.gradleProperty("androidReleaseStorePassword").orNull
|
||||
val releaseKeyPassword = providers.gradleProperty("androidReleaseKeyPassword").orNull
|
||||
val hasReleaseSigning = !releaseKeystorePath.isNullOrBlank() &&
|
||||
!releaseKeyAlias.isNullOrBlank() &&
|
||||
!releaseStorePassword.isNullOrBlank() &&
|
||||
!releaseKeyPassword.isNullOrBlank()
|
||||
val releaseBuildRequested = gradle.startParameter.taskNames.any { it.contains("release", ignoreCase = true) }
|
||||
|
||||
if (releaseBuildRequested && !hasReleaseSigning) {
|
||||
error(
|
||||
"Release signing is not configured. Configure storeFile and keyAlias in " +
|
||||
"~/.config/android/release-signing.properties and passwords in ~/.gradle/gradle.properties.",
|
||||
)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "se.ajpanton.notificationlog"
|
||||
compileSdk = 36
|
||||
@@ -13,8 +41,8 @@ android {
|
||||
applicationId = "se.ajpanton.notificationlog"
|
||||
minSdk = 34
|
||||
targetSdk = 36
|
||||
versionCode = 1
|
||||
versionName = "0.1.0"
|
||||
versionCode = project.findProperty("notificationLogVersionCode")?.toString()?.toIntOrNull() ?: 1
|
||||
versionName = project.findProperty("notificationLogVersionName")?.toString() ?: "0.1"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -23,6 +51,25 @@ android {
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
if (hasReleaseSigning) {
|
||||
create("release") {
|
||||
storeFile = file(releaseKeystorePath!!)
|
||||
storePassword = releaseStorePassword
|
||||
keyAlias = releaseKeyAlias
|
||||
keyPassword = releaseKeyPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
if (hasReleaseSigning) {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
|
||||
Reference in New Issue
Block a user