Tootsie ist ein moderner, datenschutzfreundlicher Mastodon-Client für Android: kein Tracking, keine Google-Dienste, Push-Benachrichtigungen über UnifiedPush.
Translation pending
SocialGitHub @pfitzerOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Repository information has not been collected yet.
No updates detected yet. GitHub releases will appear here.
A modern, privacy-respecting Mastodon client for Android, built with Jetpack Compose
Taken from the repository README
Loading the full README.
Read the full README ↗Tootsie is a modern, high-performance, and privacy-respecting Mastodon client for Android. Built from the ground up with Jetpack Compose and Kotlin, it follows Clean Architecture principles to provide a smooth experience for the Fediverse.
Google requires a closed test with at least 12 testers for 14 days before Tootsie can be published on Google Play. You can help:
Bugs and ideas are welcome as issues. Without the Play Store, install the APK from the latest release.
RemoteMediator) and stay fully scrollable without a network connection.Some features depend on the server version: quote posts and fetching remote replies need Mastodon 4.5 or newer, decrypting push notifications needs Mastodon 4.4 or newer. On older servers Tootsie falls back to the previous behaviour (e.g. the boost button boosts directly).
gradle/libs.versions.toml)MigrationTestHelperClone the repository:
git clone https://github.com/pfitzer/tootsie.git
cd tootsie
Open in Android Studio: Import the project and let Gradle sync.
Build & Run:
Select the app configuration and run it on your device or emulator.
./gradlew assembleDebug./gradlew assembleRelease./gradlew testDebugUnitTest./gradlew connectedDebugAndroidTest./gradlew :app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.pfitzer.tootsie.data.local.TootsieDatabaseMigrationTest
Instrumented test runs uninstall the app afterwards, which deletes its data (including logins). Add -Pandroid.injected.androidTest.leaveApksInstalledAfterRun=true to keep it installed.
CI (GitHub Actions) runs the unit tests and builds debug and release APKs; instrumented tests are not part of CI.
app/
├── schemas/ # Exported Room schemas (one JSON per database version)
└── src/main/java/com/pfitzer/tootsie/
├── TootsieApplication.kt # Hilt application, UnifiedPush callbacks
├── MainActivity.kt # Single activity hosting Compose
├── di/ # Dependency injection modules
├── data/ # Data layer
│ ├── local/ # Room (DAOs, entities, database), DataStore, credential encryption
│ ├── remote/ # Retrofit API, DTOs, interceptors, account-scoped credentials
│ ├── repository/ # Repository implementations
│ ├── mapper/ # DTO / entity ↔ domain mappers
│ └── paging/ # Paging sources, keyword filter matching
├── domain/ # Domain layer (pure Kotlin)
│ ├── model/ # Domain models
│ ├── repository/ # Repository interfaces
│ └── usecase/ # Use cases
├── presentation/ # Presentation layer
│ ├── screens/ # Screen composables & ViewModels
│ ├── components/ # Reusable UI components
│ ├── navigation/ # Navigation graph and destinations
│ ├── theme/ # Material 3 theme
│ └── util/ # UI helpers (HTML rendering, UiText, …)
├── push/ # UnifiedPush service, Web Push decryption, notifications
├── worker/ # WorkManager workers (notification polling)
└── util/ # Cross-cutting utilities
Contributions are welcome, see CONTRIBUTING.md and the Code of Conduct.
When contributing to Tootsie, please adhere to these architectural guardrails (details in CLAUDE.md):
.collect {} on DataStore.data for one-time reads as it is a hot flow and will block infinitely. Always use .first().runBlocking or suspend functions inside interceptors. Use the Provider Pattern with AtomicReference for thread-safe, synchronous access to tokens and the instance URL.init blocks or during Hilt construction. Every entry point that calls the API (splash screen, workers, push callbacks) must call AuthRepositoryImpl.ensureInitialized() first.Dispatchers.IO.strings.xml (English and German). ViewModels expose messages as UiText, never as hardcoded strings.app/schemas/ and a migration test.CredentialCipher). Requests on behalf of a specific account (push, polling) pass that account's credentials explicitly instead of using the active account.Planned features and known issues are tracked in GitHub Issues, for example:
Please report vulnerabilities privately, see SECURITY.md.
This project is licensed under the MIT License - see the LICENSE file for details.