Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# kDrive Android

## Project Snapshot

Modular Android application for kDrive cloud storage by Infomaniak. Uses MVVM architecture with Hilt DI, Realm for offline data, Ktor for API calls. Supports 3 build flavors (standard, fdroid, preprod) via Gradle composite builds with Infomaniak Core library.

**Languages**: Kotlin (100%), XML layouts (migrating to Compose)
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says “Kotlin (100%)” but the project also contains XML resources/layouts (and is “migrating to Compose”). Please adjust the wording to avoid a factual inconsistency (e.g., “Mostly Kotlin” / “Primary language: Kotlin”).

Suggested change
**Languages**: Kotlin (100%), XML layouts (migrating to Compose)
**Languages**: Primary language: Kotlin, with XML layouts/resources (migrating to Compose)

Copilot uses AI. Check for mistakes.

**Key Architecture**: MVVM + Repository pattern with Hilt dependency injection

## Root Setup Commands

```bash
# Build entire project
./gradlew assembleStandardDebug

# Run all unit tests
./gradlew test

# Build specific flavor
./gradlew assembleFdroidDebug
./gradlew assemblePreprodDebug

# Clean build
./gradlew clean

# Install debug on connected device
./gradlew installStandardDebug
```

## Universal Conventions

- **Kotlin code style**: Official style guide (enforced via `.editorconfig`)
- **Commit format**: Conventional commits recommended
- **Branch strategy**: Feature branches → main, create issue before PR
- **PR requirements**: CI must pass, at least one review for significant changes
- **Never commit**: API tokens, credentials, or local.properties values

## Security & Secrets

- Never commit `.env`, `local.properties`, or API keys to repository
- Drive API tokens stored in Room database (encrypted)
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“Drive API tokens stored in Room database (encrypted)” is too specific for what’s visible in this repo: the app uses Core’s UserDatabase (Room) for users/tokens, but the encryption aspect isn’t verifiable here. Please either link to the Core implementation that enforces encryption or soften the claim to avoid misinformation.

Suggested change
- Drive API tokens stored in Room database (encrypted)
- Drive API tokens stored in Core's Room-backed user database

Copilot uses AI. Check for mistakes.
- User files cached in Realm with offline support
- Bug tracker credentials defined in build.gradle.kts (safe values only)

## JIT Index

### Module Structure

- **Main App**: `app/` → [see app/AGENTS.md](app/AGENTS.md)
- **Core Libraries**: `Core/` → [see Core/AGENTS.md](Core/AGENTS.md)
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This links to Core/AGENTS.md, but Core is a git submodule and this repository snapshot doesn’t contain that file. Unless this PR also bumps the Core submodule to a commit that adds Core/AGENTS.md, the link will be broken; please ensure the submodule reference is updated (or adjust/remove the link until it exists).

Suggested change
- **Core Libraries**: `Core/`[see Core/AGENTS.md](Core/AGENTS.md)
- **Core Libraries**: `Core/`submodule contents live under `Core/`

Copilot uses AI. Check for mistakes.

### Core Sub-modules (Composite Build)

Key modules consumed via Gradle composite builds:

- `Core:Auth` - OAuth2 authentication flow
- `Core:Network` - Ktor HTTP client configuration
- `Core:Ui:View` - Shared UI components (XML-based)
- `Core:Ui:Compose` - Shared Compose components
- `Core:Common` - Extensions and utilities
- `Core:Legacy` - Legacy support module

### Quick Find Commands

```bash
# Find ViewModels
rg -n "class.*ViewModel" app/src/ Core/

# Find Hilt-injected components
rg -n "@AndroidEntryPoint|@HiltViewModel" app/src/main/

# Search API endpoints
rg -n "ApiRepository|suspend fun.*Api" app/src/main/

# Find Realm models
rg -n "open class.*RealmObject|@RealmClass" app/src/main/

# Locate UI fragments
rg -n "class.*Fragment" app/src/main/java/com/infomaniak/drive/ui/

# Find test files
find . -name "*Test.kt" -o -name "*Test.java"

# Search string resources
rg -n "R\.string\." app/src/main/
```

## Build Flavors

| Flavor | Description | API Endpoints |
|----------|--------------------------------|-------------------------------------------------------|
| standard | Google Play (default) | Production kDrive API |
| fdroid | F-Droid release | Production API, no proprietary dependencies |
| preprod | Pre-production testing | kdrive.preprod.dev.infomaniak.ch staging servers |

## Definition of Done

- [ ] Code compiles without warnings: `./gradlew compileStandardDebugKotlin`
- [ ] Unit tests pass: `./gradlew test`
- [ ] No hardcoded strings in UI (use `strings.xml`)
- [ ] New features include proper ViewModel separation
- [ ] Hilt DI used for dependencies (no manual instantiation)
- [ ] Background work uses WorkManager (not raw threads)
- [ ] File operations use kDrive-specific utilities
233 changes: 233 additions & 0 deletions app/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# kDrive Main App

## Package Identity

Primary kDrive Android application module. Handles cloud storage synchronization, file management, sharing, and collaboration
features. Uses MVVM architecture with Hilt DI, Realm offline cache, and Ktor for API calls.

## Setup & Run

```bash
# Build debug APK
./gradlew app:assembleStandardDebug

# Install on device
./gradlew app:installStandardDebug

# Run unit tests
./gradlew app:test

# Build F-Droid flavor
./gradlew app:assembleFdroidDebug

# Build pre-production
./gradlew app:assemblePreprodDebug
```

## Architecture Patterns

### MVVM Structure

```
app/src/main/java/com/infomaniak/drive/
├── data/ # Repositories, API, cache, models
├── ui/ # Activities, Fragments, ViewModels
├── di/ # Hilt modules (ApplicationModule, ActivityModule)
├── extensions/ # Kotlin extensions
└── utils/ # Utility classes
```

### Data Layer

- **api/**: Ktor API clients and repository implementations
- **cache/**: Realm database configuration and DAOs
- **models/**: Data classes (File, Drive, User, etc.)
- **sync/**: Background synchronization logic
- **services/**: Foreground services for uploads/downloads

### Key Dependencies Management

- Hilt modules: `di/ApplicationModule.kt`, `di/ActivityModule.kt`
- Token provider: `TokenInterceptorListenerProvider.kt`
- App initialization: `MainApplication.kt`

## Patterns & Conventions

### ViewModel Creation

- **DO**: Inject dependencies via constructor
```kotlin
@HiltViewModel
class FileListViewModel @Inject constructor(
private val fileRepository: FileRepository,
savedStateHandle: SavedStateHandle
) : ViewModel()
```
Comment thread
benjaminVadon marked this conversation as resolved.
- **DON'T**: Use AndroidViewModel unless absolutely necessary (e.g., need Application context)

### Fragment Pattern

- **DO**: Use Navigation Component with Safe Args
- Example: `ui/fileList/FileListFragment.kt`
- **DON'T**: Use FragmentTransaction directly

### Background Work

- **DO**: Use WorkManager for sync operations
- Location: `data/sync/` for workers
- **DON'T**: Use raw Threads or AsyncTask

### API Calls

- **DO**: Use repository pattern with Ktor
- Example: `data/api/ApiRepository.kt`
- Wrapper: `data/api/ApiService.kt`
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ApiService.kt is referenced as an API wrapper, but there is no data/api/ApiService.kt in this module (only ApiRepository.kt, ApiRoutes.kt, etc.). Please update the reference to an existing entry point or add the missing wrapper file so the guide doesn’t point readers to a non-existent path.

Suggested change
- Wrapper: `data/api/ApiService.kt`
- Entry point: `data/api/ApiRepository.kt`

Copilot uses AI. Check for mistakes.

### Realm Models

- **DO**: Use `@RealmClass` annotation, open classes
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section says Realm models should use @RealmClass, but data/models/File.kt (the provided example) is an open class ... : RealmObject() without @RealmClass. Please reword to match actual usage (e.g., open class : RealmObject() and @RealmClass only where needed, such as embedded objects).

Suggested change
- **DO**: Use `@RealmClass` annotation, open classes
- **DO**: Define Realm models as `open class ... : RealmObject()`
- Use `@RealmClass` only where needed (for example, embedded Realm objects)

Copilot uses AI. Check for mistakes.
- Example: `data/models/File.kt`
- **DON'T**: Use Room for file data (Realm only)
Comment thread
FabianDevel marked this conversation as resolved.

### Activity Injection

- **DO**: Annotate with `@AndroidEntryPoint`
- Example: `ui/MainActivity.kt`
- **DON'T**: Manually instantiate ViewModels

### Image Loading

- **DO**: Use Coil (configured via Core)
- **DON'T**: Use Glide or other loaders

## Key Files Reference

### Entry Points

- Application: `MainApplication.kt` (Hilt entry)
- Main Activity: `ui/MainActivity.kt`
- File List: `ui/fileList/FileListFragment.kt`

### ViewModels (35+ files)

- Main: `ui/MainViewModel.kt`
- File List: `ui/fileList/FileListViewModel.kt`
- Search: `ui/fileList/SearchViewModel.kt`
- Settings: `ui/menu/settings/SettingsViewModel.kt`

### Data Repositories

- API service: `data/api/ApiService.kt`
- File repository: `data/api/ApiRepository.kt`
- Cache manager: `data/cache/FileController.kt`

### Models

- File: `data/models/File.kt`
- Drive: `data/models/Drive.kt`
- User: `data/models/UiSettings.kt`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Correct the file path reference for the User model. The current reference to UiSettings.kt appears to be a copy-paste error from the line below it, as UiSettings typically stores UI preferences rather than User account data, which would mislead developers navigating the codebase. [general, importance: 6]

Suggested change
- User: `data/models/UiSettings.kt`
- User: `data/models/User.kt`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The User model is incorrectly mapped to UiSettings.kt, which likely contains UI preferences rather than user account data. This documentation error could mislead developers or AI agents attempting to locate the User data class. Update the reference to point to the actual User model file (typically data/models/User.kt or similar). [general, importance: 7]

Suggested change
- User: `data/models/UiSettings.kt`
- User: `data/models/User.kt`

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “Models” list labels UiSettings.kt as “User”, but the app’s user model comes from Core (com.infomaniak.core.auth.models.user.User) and UiSettings is preference storage. Please point “User” to the correct type/file (or rename this entry) to avoid misleading new contributors.

Suggested change
- User: `data/models/UiSettings.kt`
- UI settings: `data/models/UiSettings.kt`
- User (Core): `com.infomaniak.core.auth.models.user.User`

Copilot uses AI. Check for mistakes.

### Sync & Services

- Sync adapter: `data/sync/SyncAdapter.kt`
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data/sync/SyncAdapter.kt is referenced here, but app/src/main/java/com/infomaniak/drive/data/sync/ currently contains workers (e.g., MediaObserverWorker.kt) and no SyncAdapter.kt. Please update this reference to an existing worker/entry point for sync.

Suggested change
- Sync adapter: `data/sync/SyncAdapter.kt`
- Sync worker: `data/sync/MediaObserverWorker.kt`

Copilot uses AI. Check for mistakes.
- Upload service: `data/services/UploadWorker.kt`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The entry incorrectly labels a WorkManager Worker as a Service. UploadWorker extends Worker or CoroutineWorker, not Service. Correct the terminology to prevent confusion when developers search for background upload components using the wrong architectural pattern. [general, importance: 6]

Suggested change
- Upload service: `data/services/UploadWorker.kt`
- Upload worker: `data/services/UploadWorker.kt`


### String resources

- After every change in strings.xml files, raise a warning for the user that he MUST mirror the changes to Loco
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wording: “raise a warning for the user that he MUST …” is unclear (who raises the warning?) and uses gendered language. Please rephrase to a concrete action (e.g., add a PR checklist item) and use neutral wording (“they/the user”, avoid all-caps MUST unless it’s an enforced rule).

Suggested change
- After every change in strings.xml files, raise a warning for the user that he MUST mirror the changes to Loco
- After every change in `strings.xml` files, remind the user to mirror the changes to Loco

Copilot uses AI. Check for mistakes.

## UI Organization

```bash
ui/
├── fileList/ # File browsing (FileListFragment, FileAdapter)
├── menu/ # Navigation drawer, settings, trash
│ └── settings/ # Sync settings, preferences
├── home/ # Home dashboard
├── login/ # Authentication flow
├── dropbox/ # Dropbox creation/management
├── bottomSheetDialogs/ # Bottom sheets (share, move, etc.)
├── addFiles/ # New folder, upload options
├── publicShare/ # Public sharing UI
└── selectPermission/ # Permission selection
```

Comment thread
FabianDevel marked this conversation as resolved.
## JIT Index

### Find ViewModels

```bash
rg -n "@HiltViewModel|class.*ViewModel" app/src/main/
```

### Find API endpoints

```bash
# Repository methods
rg -n "suspend fun.*upload|suspend fun.*delete" app/src/main/java/com/infomaniak/drive/data/api/

# Data models
rg -n "open class" app/src/main/java/com/infomaniak/drive/data/models/
```

### Find UI components

```bash
# Fragments
rg -n "class.*Fragment" app/src/main/java/com/infomaniak/drive/ui/

# Activities
rg -n "class.*Activity" app/src/main/java/com/infomaniak/drive/ui/

# Adapters
rg -n "class.*Adapter" app/src/main/java/com/infomaniak/drive/
```

### Search strings

```bash
rg -n "R\.string\." app/src/main/java/ | head -20
```

### Find extensions

```bash
ls app/src/main/java/com/infomaniak/drive/extensions/
```

## Testing

### Unit Tests

- Location: `app/src/test/`
- Framework: JUnit 5 (via `junit5` plugin)
- Example: `app/src/test/kotlin/RootFileTreeCategoryTest.kt`

### Instrumented Tests

- Location: `app/src/androidTest/`
- Env setup: Copy `Env-Example` to `Env.kt` (disable 2FA for test account)

### Run Commands

```bash
# All tests
./gradlew app:test

# Single test class
./gradlew app:test --tests "com.infomaniak.drive.RootFileTreeCategoryTest"
```

## Common Gotchas

- **Realm transactions**: Always use `realm.executeTransactionAwait()` with coroutines
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The executeTransactionAwait() method belongs to the deprecated Realm Java Coroutines extension. Modern Realm Kotlin (io.realm.kotlin) uses realm.write { } for suspend functions. Update this guidance to prevent compilation errors when implementing Realm transactions with coroutines. [general, importance: 7]

Suggested change
- **Realm transactions**: Always use `realm.executeTransactionAwait()` with coroutines
- **Realm transactions**: Always use `realm.write { }` for suspend operations or `realm.writeBlocking { }` for synchronous operations

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

realm.executeTransactionAwait() is mentioned as the required pattern, but this symbol doesn’t exist anywhere in this repository (no matches in source). Please update this “Realm transactions” gotcha to the actual coroutine-friendly transaction API used in the app (or add the missing helper if it’s intended).

Suggested change
- **Realm transactions**: Always use `realm.executeTransactionAwait()` with coroutines
- **Realm transactions**: Use the coroutine-friendly Realm write/transaction pattern already established in the surrounding app code; do not reference `realm.executeTransactionAwait()` unless that helper is actually added to the project

Copilot uses AI. Check for mistakes.
- **File operations**: All file I/O must use kDrive utilities in `utils/` package
- **Background sync**: Requires `FOREGROUND_SERVICE_DATA_SYNC` permission for Android 14+
- **Media permissions**: Android 13+ uses granular `READ_MEDIA_*` permissions
- **Hilt modules**: Keep provider methods in `di/ApplicationModule.kt`

## Pre-PR Checks

```bash
./gradlew app:assembleStandardDebug && ./gradlew app:testStandardDebugUnitTest
```
Loading