Bundl is a modern Android application that helps you take control of your notifications by intelligently bundling them and delivering them on your schedule.
- Smart Rule-Based Filtering: Create custom rules to bundle notifications from specific apps
- Blacklist & Whitelist Modes: Choose whether to bundle specific notifications or allow only certain ones
- Filter Strings: Add optional text filters to match specific notification content
- App Name Display: View friendly app names instead of technical package names
- Custom Schedules: Set multiple times throughout the day for automatic notification delivery
- 24-Hour Time Picker: Native Material 3 time picker for easy schedule management
- Enable/Disable Toggles: Control individual schedules without deleting them
- Boot Persistence: Schedules automatically restore after device reboot
- View All Bundled Notifications: See all notifications that have been intercepted
- App Icons & Names: Beautiful display with app icons and human-readable names
- Detailed Information: View full notification details including title, text, and timestamps
- Search & Filter: Find notifications by app name or package name
- Easy App Selection: Pick apps from a searchable list with icons
- Visual Rule Cards: See rules displayed with app names and icons
- Edit & Delete: Manage your rules with intuitive controls
- System & User Apps: Access all installed applications
- Deliver Now Button: Instantly deliver all bundled notifications at any time
- Grouped by App: Notifications are intelligently grouped by application
- Expandable Summaries: Inbox-style notifications with up to 5 items visible
- Mark as Read: Persistent notifications with action button to dismiss
[Add screenshots here]
- Minimum SDK: Android 8.0 (API 26)
- Target SDK: Android 14 (API 36)
- Permissions Required:
POST_NOTIFICATIONS- For delivering bundled notifications (Android 13+)RECEIVE_BOOT_COMPLETED- For rescheduling alarms after rebootSCHEDULE_EXACT_ALARM- For precise scheduled deliveryWAKE_LOCK- For reliable alarm triggersQUERY_ALL_PACKAGES- For viewing all installed apps- Notification Listener Access - For intercepting notifications (granted via Settings)
- Clone the repository:
git clone https://github.com/eliasfloreteng/bundl.git
cd bundl-
Open the project in Android Studio
-
Build and run:
./gradlew assembleDebug- Install the APK on your device:
adb install app/build/outputs/apk/debug/app-debug.apkBuilding & Publishing Production APK (Click to expand)
-
Generate Signed Bundle/APK:
- Click
Build→Generate Signed Bundle / APK - Select
Android App Bundle(for Google Play) orAPK - Click
Next
- Click
-
Create or Select Keystore:
If you don't have a keystore (first time):
- Click
Create new... - Key store path: Choose location (e.g.,
bundl-release-key.jks) - Password: Enter a strong password
- Key alias:
bundl(or your preferred name) - Key password: Enter a strong password (can be same or different)
- Validity: 25 years (default)
- Certificate: Fill in your details
- Click
OK
⚠️ IMPORTANT: Save these passwords securely! You cannot update your app without them.If you already have a keystore:
- Click
Choose existing... - Select your
.jksfile - Enter passwords and alias
- Click
Next
- Click
-
Build Configuration:
- Destination folder: Where to save the output file
- Build Variants: Select
release - For AAB: Check
Export encrypted key(required for Play App Signing) - Click
Finish
-
Wait for Build:
- Android Studio will build your signed APK/AAB
- A notification will appear when complete
- Click
locateto find the file
-
Output Locations:
- AAB:
app/release/app-release.aab - APK:
app/release/app-release.apk
- AAB:
- Create a keystore (one-time setup):
keytool -genkey -v -keystore bundl-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias bundlFollow the prompts to set:
- Keystore password
- Key password
- Your name and organization details
- Create
keystore.propertiesin the project root:
storePassword=YOUR_KEYSTORE_PASSWORD
keyPassword=YOUR_KEY_PASSWORD
keyAlias=bundl
storeFile=../bundl-release-key.jks.gitignore.
- Update
app/build.gradle.ktsto load signing config:
// Add at the top of the file
val keystorePropertiesFile = rootProject.file("keystore.properties")
val keystoreProperties = Properties()
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
// ... existing config ...
signingConfigs {
create("release") {
storeFile = file(keystoreProperties["storeFile"] as String)
storePassword = keystoreProperties["storePassword"] as String
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
}
}
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
signingConfig = signingConfigs.getByName("release")
}
}
}- Build the release APK:
./gradlew assembleReleaseThe signed APK will be at: app/build/outputs/apk/release/app-release.apk
- Or build an AAB (Android App Bundle) for Google Play:
./gradlew bundleReleaseThe AAB will be at: app/build/outputs/bundle/release/app-release.aab
- Google Play Developer Account ($25 one-time fee)
- Signed AAB file (from step 5 above)
- App assets:
- App icon (512x512 PNG)
- Feature graphic (1024x500 PNG)
- Screenshots (at least 2, up to 8)
- Privacy policy URL (if app collects data)
-
Go to Google Play Console: https://play.google.com/console
-
Create a new app:
- Click "Create app"
- Enter app name, default language, app/game type
- Declare if it's free or paid
-
Complete the Store Listing:
- App name and short description
- Full description (up to 4000 characters)
- App icon and feature graphic
- Screenshots for phone, tablet, etc.
- Categorization and tags
- Contact details and privacy policy
-
Set up Content Rating:
- Complete the questionnaire
- Get rating certificates (required before publishing)
-
Set Target Audience and Content:
- Age ranges
- Content declarations
- Ads presence
-
Declare Data Safety:
- For Bundl, you should declare:
- ✅ No data collected and shared with third parties
- ✅ Data stored locally on device
⚠️ App accesses notifications (for functionality)
- For Bundl, you should declare:
-
Complete App Access:
- Add instructions for testing notification access
- Provide test credentials if needed
-
Set up Pricing and Distribution:
- Select countries
- Pricing (free recommended)
- Distribution consent
-
Upload the AAB:
- Go to "Production" → "Create new release"
- Upload
app-release.aab - Write release notes
- Review and rollout
-
Handle Special Permissions:
QUERY_ALL_PACKAGES requires justification:
- Go to "App content" → "Sensitive app permissions"
- Declare usage of
QUERY_ALL_PACKAGES - Provide justification:
"Bundl is a notification management app that requires QUERY_ALL_PACKAGES to display a list of installed apps to the user. Users need to select which apps' notifications should be bundled. This permission is essential for the core functionality of the app."
- Submit a screen recording showing:
- App picker feature in action
- User selecting apps from the list
- How it's used for notification bundling
- Submit for Review:
- Review summary
- Submit app for review
- Wait for approval (typically 1-7 days)
- Monitor: Check crash reports and user reviews
- Update: Use same signing key for all future updates
- Respond: Reply to user reviews promptly
- Write clear, detailed feature descriptions
- Provide comprehensive privacy policy
- Include good quality screenshots showing all features
- Record a demo video showing permission usage
- Test thoroughly before submitting
- Respond quickly to review questions
- Grant Notification Permission: On first launch (Android 13+), you'll be prompted to allow notification posting
- Enable Notification Access:
- Toggle "Bundling" on the Home screen
- You'll be redirected to Settings
- Enable notification access for Bundl
- Create Rules: Navigate to the Rules tab and add apps you want to bundle
- Set Schedules (Optional): Go to the Schedule tab to set automatic delivery times
- Navigate to the Rules tab
- Tap the + (Add) button
- Click the list icon to pick an app from the searchable list
- Choose a mode:
- BLACKLIST: Bundle all notifications from this app (or only those matching the filter)
- WHITELIST: Allow only notifications matching the filter
- Add an optional filter string to match specific notification content
- Tap Save
- Navigate to the Schedule tab
- Tap the + (Add) button
- Use the time picker to select a delivery time
- Tap Add
- Toggle schedules on/off as needed
- Delete schedules with the trash icon
Bundl follows modern Android development best practices:
- UI Framework: Jetpack Compose with Material 3
- Architecture: MVVM (Model-View-ViewModel)
- Database: Room with Flow-based reactive queries
- State Management: Kotlin StateFlow and Coroutines
- Preferences: DataStore (type-safe)
- Image Loading: Coil 3.x
- Navigation: NavigationSuiteScaffold with adaptive layouts
app/src/main/java/se/floreteng/bundl/
├── MainActivity.kt # Entry point
├── HomeScreen.kt # Main screen with bundling toggle
├── HistoryScreen.kt # Notification history view
├── ScheduleScreen.kt # Schedule management
├── SettingsScreen.kt # App rules management
├── BundlDatabase.kt # Room database
├── BundlNotificationListenerService.kt # Notification interception
├── BootReceiver.kt # Reboot handling
├── NotificationActionReceiver.kt # Notification actions
├── ScheduledDeliveryReceiver.kt # Scheduled delivery
├── apprule/ # App rule feature
│ ├── AppRule.kt
│ ├── AppRuleDao.kt
│ ├── AppRuleRepository.kt
│ ├── AppRuleViewModel.kt
│ ├── AppRuleDialog.kt
│ └── ...
├── notifications/ # Notification feature
│ ├── Notification.kt
│ ├── NotificationDao.kt
│ ├── NotificationRepository.kt
│ ├── NotificationViewModel.kt
│ └── ...
├── schedule/ # Schedule feature
│ ├── Schedule.kt
│ ├── ScheduleDao.kt
│ ├── ScheduleRepository.kt
│ ├── ScheduleViewModel.kt
│ └── ...
├── preferences/ # App preferences
│ └── PreferencesManager.kt
└── utils/ # Utilities
├── AppInfoUtil.kt
├── NotificationAccessUtil.kt
├── NotificationDeliveryUtil.kt
└── ScheduleAlarmUtil.kt
- Language: Kotlin
- UI: Jetpack Compose with Material 3
- Database: Room 2.8.3 with KSP
- Async: Kotlin Coroutines & Flow
- DI: Manual ViewModelFactory
- Image Loading: Coil 3.0.4
- Build System: Gradle with Kotlin DSL
- Intercepts all system notifications
- Checks against user-defined rules
- Cancels matching notifications
- Stores cancelled notifications in database
- Schedules daily repeating alarms
- Handles device reboot restoration
- Delivers notifications at scheduled times
- Clears database after delivery
- Searchable list of all installed apps
- App icons loaded with Coil
- Filter by app name or package name
- Real-time app name resolution
Bundl takes privacy seriously:
- Local Storage Only: All data is stored locally on your device
- No Internet: The app requires no internet connection
- No Analytics: No tracking or analytics are collected
- No Ads: Completely ad-free
- Open Source: Code is available for review
- Notification Access: Required to intercept notifications from other apps
- Post Notifications: Needed to deliver bundled notification summaries
- Query All Packages: Allows you to see all installed apps in the app picker
- Alarm Permissions: For scheduling automatic delivery at specified times
- Boot Completed: To restore schedules after device restart
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow Kotlin coding conventions
- Use meaningful variable and function names
- Add comments for complex logic
- Keep functions small and focused
- On Android 14+, some system dialogs may appear on top of notification permission requests
- App list may take a moment to load on first opening the app picker
- Notification templates for custom formats
- Export/import rules and schedules
- Notification statistics and analytics
- Dark/Light theme customization
- Widget support
- Backup to cloud storage
MIT License
- Built with Jetpack Compose
- Icons from Material Icons
- Image loading by Coil
For issues, questions, or suggestions, please open an issue on GitHub.
This app intercepts and manages notifications from other apps on your device. By using this app, you acknowledge that:
- The app requires extensive permissions to function properly
- Notifications are stored locally and processed on-device
- The app may not work perfectly with all notification types
- Use at your own risk
Made with ❤️ using Kotlin and Jetpack Compose