Setwork is a minimal, offline-first task management application for Android. It is written entirely in Kotlin and built on top of Jetpack Compose, with no cloud dependency, no subscription model, and no telemetry of any kind. All data is stored locally on the device.
The project is structured as a multi-module ecosystem — three independent repositories, each with a clearly bounded responsibility, consumed together to form the complete application.
The Setwork ecosystem is composed of three repositories:
| Repository | Role |
|---|---|
Setwork |
Main application — UI, navigation, settings, feature screens |
Setwork-Orchestrator |
Library module — notification scheduling and date rendering |
Setwork-Provider |
Library module — data provision and content sourcing |
Each module is independently versioned and independently releasable. The Orchestrator is currently at v1.0.7, which means it has already iterated beyond the main app's v1.0.0 — a incapsulated multi-module systems.
The application follows Clean Architecture principles, organized by feature rather than by layer.
Each feature module within the main app contains its own data, domain, and presentation sub-packages. Shared infrastructure — repositories, utilities, base classes — lives in common/.
The dependency injection strategy is a Service Locator via AppServiceLocator, rather than a framework like Hilt or Koin. This is a conscious trade-off: less compile-time safety in exchange for reduced complexity, faster build times, and fewer framework abstractions between the engineer and the code. For a project of this scale, it is a defensible decision.
com.designlife.justdo
|
+-- common/
| +-- data/ # Repositories, Room database, DataStore
| +-- domain/ # Use cases, domain models, business rules
| +-- presentation/ # Reusable Compose components
| +-- utils/ # AppServiceLocator, SoftwareUpdateManager
|
+-- container/presentation/ # Navigation shell, ContainerFragment
+-- deck/presentation/ # Deck/board view feature
+-- home/
| +-- domain/usecase/ # Home-specific business logic
| +-- presentation/ # HomeFragment, HomeViewModel
+-- note/presentation/ # Note feature
+-- permission/ # PermissionHandler, RequestPermissions
+-- settings/presentation/
| +-- components/ # Settings UI components
| +-- entity/ # Settings data models
| +-- enums/ # AppTheme, FontSize, ListItemHeight enums
| +-- events/ # Settings UI event definitions
| +-- viewmodel/ # SettingViewModel
+-- task/ # Core task feature
+-- ui.theme/ # Compose theme: colors, typography, sizing
|
+-- MainActivity.kt # Single-Activity entry point
Setwork-Orchestrator/
+-- app/ # Sample/test app harness
+-- orchestrator/ # The library module itself
+-- notification/ # Scheduling, BroadcastReceivers
+-- date/ # Date rendering and formatting logic
The Orchestrator is the most interesting module architecturally. Notification scheduling on Android is non-trivial — it must survive device reboots, respect Doze mode, handle exact alarm permissions on API 31+, and behave correctly when the system reclaims resources. Extracting this logic into a dedicated, independently versioned module is the correct approach. It can be tested in isolation, updated without redeploying the main app, and reasoned about as a distinct system.
| Concern | Technology | Notes |
|---|---|---|
| Language | Kotlin | 100% of all three repositories |
| UI | Jetpack Compose + XML | Hybrid: Compose within Fragments |
| Navigation | Navigation Component | Single-Activity, Fragment destinations |
| Architecture | Clean Architecture | Feature-modular, not layer-modular |
| Dependency Provision | Service Locator | AppServiceLocator, no DI framework |
| Asynchrony | Kotlin Coroutines + Flow | collectLatest for preference streams |
| Local Storage | Room (SQLite) | CursorWindow resized to 100MB |
| Preferences | DataStore | Typed async preference storage |
| Scheduling | Setwork-Orchestrator | Notifications, alarms, date logic |
| Updates | SoftwareUpdateManager | In-app update detection |
| Build System | Gradle (Groovy DSL) | Multi-module configuration |
A note on the 100MB CursorWindow: the default SQLite cursor window in Android is 2MB. The explicit resize in MainActivity suggests that the application deals with potentially large result sets — possibly from bulk task imports, long task histories, or note content. This is worth understanding before contributing any code that touches the database layer.
Distribution — coming soon:
When installing from a GitHub release APK, you will need to permit installation from unknown sources on your device: Settings > Apps > Special App Access > Install Unknown Apps.
- Android Studio Hedgehog (2023.1.1) or later
- Android SDK API 26 minimum (Android 8.0)
- JDK 11 or higher
- Git
git clone https://github.com/itsfaraz/Setwork.git
git clone https://github.com/itsfaraz/Setwork-Orchestrator.git
git clone https://github.com/itsfaraz/Setwork-Provider.gitcd Setwork
# Debug build
./gradlew assembleDebug
# Install to connected device
./gradlew installDebug
# Unit tests
./gradlew test
# Release build
./gradlew assembleReleaseBuild outputs:
app/build/outputs/apk/debug/app-debug.apk
app/build/outputs/apk/release/app-release.apk
| Permission | Justification |
|---|---|
POST_NOTIFICATIONS |
Required to deliver scheduled task reminders |
READ_EXTERNAL_STORAGE |
Required to import task backups from device storage |
WRITE_EXTERNAL_STORAGE |
Required to write task export files to device storage |
The application requests no location, camera, microphone, or network permissions. The only network activity is the in-app update check via SoftwareUpdateManager. There are no analytics SDKs, no crash reporters that phone home, and no advertising identifiers.
Setwork is a production-quality open-source project, and the codebase was written to be read. If you are a student of Android development or an engineer evaluating modern Android patterns, this section maps the concepts you will encounter to where they live in the code.
Single-Activity architecture with Jetpack Navigation
MainActivity is the only Activity in the application. All screens are Fragments, connected through a navigation graph managed by NavHostFragment. This is the current recommended approach from Google and reflects how most modern Android apps should be structured. Understanding why this replaced the multi-Activity model — lifecycle complexity, back stack management, shared ViewModel scoping — is foundational knowledge for any Android engineer.
Jetpack Compose within Fragments
The app uses a hybrid UI approach: Fragments for navigation, Compose for rendering. MainActivity contains a ComposeView used specifically to detect and react to dark mode changes. Individual feature screens use Compose within their Fragment's onCreateView. This hybrid pattern is common in apps migrating from XML to Compose incrementally, and understanding it is practically valuable.
Reactive preference management with DataStore and Flow
SettingViewModel exposes preferences as a Flow. MainActivity collects these with collectLatest on a background coroutine, and applies changes — font size, list item height, theme — to system-level UI state. This is the correct pattern for settings that need to propagate globally without manual callbacks or event buses.
Service Locator pattern
AppServiceLocator provides dependencies on demand without a framework. This is worth studying not because it is always the right choice — Hilt is often better — but because understanding what a DI framework does under the hood requires having written something like a Service Locator first. It is also a legitimate choice in applications where build time is a priority.
Runtime permission handling
PermissionHandler centralizes all permission logic away from MainActivity. The permission launcher uses ActivityResultContracts.RequestMultiplePermissions(), which is the modern, non-deprecated approach. The commented-out MANAGE_EXTERNAL_STORAGE and READ_MEDIA_* permissions are also instructive — they show the evolution of Android's storage permission model across API levels.
CursorWindow resizing
The resizeCursorWindow() call in MainActivity uses reflection to expand the SQLite cursor window to 100MB. This is an advanced, somewhat obscure Android technique. It implies that the application's data layer can produce large query results. Any engineer who plans to work on the database layer should understand this before writing queries, as the default 2MB window will cause CursorWindowAllocationException if exceeded.
Standalone Android library module
The Orchestrator is a proper Gradle library module with its own build configuration, versioning, and release cycle. It demonstrates how to extract logic that belongs to the application but not to the application's UI into a reusable, independently deployable artifact.
Notification scheduling on modern Android
Scheduling reliable notifications on Android is harder than it appears. Exact alarms require the SCHEDULE_EXACT_ALARM permission on API 31+. Notifications must be rescheduled after device reboot via a BOOT_COMPLETED receiver. Doze mode and App Standby buckets affect delivery timing. The Orchestrator module encapsulates all of this complexity behind a clean interface.
Date rendering logic as a first-class concern
Date formatting and relative time display ("Today", "Tomorrow", "3 days ago") is the kind of logic that often gets scattered across ViewModels and UI components. Centralizing it in the Orchestrator module makes it testable, reusable, and consistent across the entire application.
For an engineer approaching this codebase seriously, the following order is suggested:
1. Read MainActivity.kt in full — understand the app's bootstrap sequence
2. Examine the navigation graph — understand how the app moves between screens
3. Read SettingViewModel — understand how preferences are modeled and observed
4. Read AppServiceLocator — understand the dependency provision strategy
5. Trace a single feature end-to-end (e.g., Home) through data > domain > presentation
6. Read the Orchestrator module — understand notification and date logic
7. Identify something missing or improvable — open an issue or a PR
Contributions are welcome from engineers at any level of experience. The only requirements are that contributions are thoughtful, that they respect the existing architectural decisions unless there is a clear reason to change them, and that they come with a clear explanation of intent.
# Fork the repository on GitHub, then:
git clone https://github.com/YOUR_USERNAME/Setwork.git
cd Setwork
git checkout -b fix/your-fix-name
# or
git checkout -b feature/your-feature-name
# Make your changes
git commit -m "Fix: clear description of the change"
# or
git commit -m "Add: clear description of the addition"
git push origin fix/your-fix-name
# Open a Pull Request on GitHubFix: correct notification not firing after device reboot
Add: subtask support with parent-child task relationship
Refactor: extract date formatting to a dedicated utility class
Docs: update architecture section of README
Test: add unit tests for TaskRepository
A good contribution does one thing well. It does not mix refactoring with new features. It does not introduce new dependencies without justification. It includes a clear description in the PR of what the change does, why it is needed, and how it was tested. It respects the existing module boundaries — changes to notification logic belong in the Orchestrator, not scattered through the main app.
Defect reports and fixes. If the application behaves incorrectly, file an issue with steps to reproduce, the expected behavior, the actual behavior, your Android version, and your device model. Logcat output is always helpful.
Feature development. New features should be proposed as an issue before implementation begins. This avoids wasted effort on changes that do not align with the project direction.
Test coverage. The project would benefit significantly from expanded unit test coverage, particularly for use cases in the domain layer and for the Orchestrator's scheduling logic.
Documentation. Inline documentation of non-obvious code, architecture decision records (ADRs), and improvements to this README are all valued.
Translations. The application is not yet internationalized, but contributions toward i18n infrastructure and language files are welcome.
Accessibility. Content descriptions, TalkBack support, and minimum touch target sizing are areas where contributions would have meaningful user impact.
- Follow the official Kotlin Coding Conventions
- Match the feature-module Clean Architecture pattern in the existing codebase
- Functions should be small and have a single responsibility
- Public APIs — even internal module APIs — should be documented
- Do not introduce third-party dependencies without opening an issue for discussion first
The following items are planned or under consideration. None of these are guaranteed to ship in any particular order. Contributions toward any of them are welcome.
| Item | Notes |
|---|---|
| Task View Screen | navigate to deck or notes |
| Scheduling Reminder | Task, Notes (Every if any) |
| Subtasks | Parent-child task hierarchy |
| Task, Note | Task & Note widget |
| Task priority levels | High / medium / low with visual differentiation |
| Search and filter | Full-text search across tasks and notes |
| Productivity metrics | Completion rates, streaks, historical view |
| Internationalization | i18n infrastructure + initial language files |
| Test coverage | Unit and UI test suite |
| Optional cloud sync | User-controlled, no default data collection |
| Tablet and foldable layouts | Adaptive UI for large screens |
Setwork is free and open source. It is maintained in spare time. If the project has been useful to you — as a user, a learner, or a reference — the following forms of support are appreciated.
Star the repository. It costs nothing and directly helps other engineers discover the project.
Share it. In developer communities, study groups, or with colleagues who might find it useful.
Contribute. See the Contributing section above. Code, tests, documentation, and translations are all valued.
Donate via Bitcoin. If you would like to support the project financially:
BTC Address: bc1q3d52v9tk2hhlnsmj08es2atkq8zgxeeg69ykth
Donations go directly toward development time and tooling costs.
Built and maintained by Faraz Sheikh, a software developer based in India.
MIT License
Copyright (c) 2026 Faraz Sheikh
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.