Compare commits
44 Commits
realmasync
...
master
@ -0,0 +1,139 @@ |
|||||||
|
# CLAUDE.md |
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
||||||
|
|
||||||
|
## Project Overview |
||||||
|
|
||||||
|
This is **Poker Analytics**, a comprehensive Android application for tracking and analyzing poker sessions. The app supports both cash games and tournaments, providing detailed statistics, reporting, and data visualization capabilities. |
||||||
|
|
||||||
|
### Key Features |
||||||
|
- Session tracking (cash games and tournaments) |
||||||
|
- Advanced filtering and reporting |
||||||
|
- Bankroll management |
||||||
|
- Hand history import and replay |
||||||
|
- Statistical analysis and charting |
||||||
|
- Data backup and export functionality |
||||||
|
- Multi-currency support |
||||||
|
|
||||||
|
## Development Commands |
||||||
|
|
||||||
|
### Building the Project |
||||||
|
```bash |
||||||
|
./gradlew assembleStandardRelease # Build release APK |
||||||
|
./gradlew assembleStandardDebug # Build debug APK |
||||||
|
./gradlew build # Build all variants |
||||||
|
``` |
||||||
|
|
||||||
|
### Running Tests |
||||||
|
```bash |
||||||
|
./gradlew test # Run unit tests |
||||||
|
./gradlew connectedAndroidTest # Run instrumented tests (requires device/emulator) |
||||||
|
./gradlew testStandardDebugUnitTest # Run specific unit tests |
||||||
|
``` |
||||||
|
|
||||||
|
### Cleaning |
||||||
|
```bash |
||||||
|
./gradlew clean # Clean build artifacts |
||||||
|
``` |
||||||
|
|
||||||
|
### Build Configuration |
||||||
|
- **Target SDK**: 35 (Android 15) |
||||||
|
- **Min SDK**: 23 (Android 6.0) |
||||||
|
- **Build Tools**: 30.0.3 |
||||||
|
- **Kotlin Version**: 1.9.24 |
||||||
|
- **Realm Schema Version**: 14 |
||||||
|
|
||||||
|
## Architecture Overview |
||||||
|
|
||||||
|
### Package Structure |
||||||
|
The main source code is organized under `app/src/main/java/net/pokeranalytics/android/`: |
||||||
|
|
||||||
|
#### Core Components |
||||||
|
- **`model/`** - Data models and business logic |
||||||
|
- `realm/` - Realm database models (Session, Bankroll, Result, etc.) |
||||||
|
- `filter/` - Query system for filtering sessions |
||||||
|
- `migrations/` - Database migration handling |
||||||
|
- `handhistory/` - Hand history data structures |
||||||
|
|
||||||
|
- **`ui/`** - User interface components |
||||||
|
- `activity/` - Main activities (HomeActivity, SessionActivity, etc.) |
||||||
|
- `fragment/` - UI fragments organized by feature |
||||||
|
- `adapter/` - RecyclerView adapters and data sources |
||||||
|
- `modules/` - Feature-specific UI modules |
||||||
|
|
||||||
|
- **`calculus/`** - Statistics and calculation engine |
||||||
|
- Core calculation logic for poker statistics |
||||||
|
- Report generation system |
||||||
|
- Performance tracking |
||||||
|
|
||||||
|
- **`util/`** - Utility classes |
||||||
|
- `csv/` - CSV import/export functionality |
||||||
|
- `billing/` - In-app purchase handling |
||||||
|
- `extensions/` - Kotlin extension functions |
||||||
|
|
||||||
|
#### Key Classes |
||||||
|
- **`Session`** (`model/realm/Session.kt`): Core session data model |
||||||
|
- **`HomeActivity`** (`ui/activity/HomeActivity.kt`): Main app entry point with tab navigation |
||||||
|
- **`PokerAnalyticsApplication`**: Application class handling initialization |
||||||
|
|
||||||
|
### Database Architecture |
||||||
|
The app uses **Realm** database with these key entities: |
||||||
|
- `Session` - Individual poker sessions |
||||||
|
- `Bankroll` - Bankroll management |
||||||
|
- `Result` - Session results and statistics |
||||||
|
- `ComputableResult` - Pre-computed statistics for performance |
||||||
|
- `Filter` - Saved filter configurations |
||||||
|
- `HandHistory` - Hand-by-hand game data |
||||||
|
|
||||||
|
### UI Architecture |
||||||
|
- **MVVM pattern** with Android Architecture Components |
||||||
|
- **Fragment-based navigation** with bottom navigation tabs |
||||||
|
- **Custom RecyclerView adapters** for data presentation |
||||||
|
- **Material Design** components |
||||||
|
|
||||||
|
## Key Technologies |
||||||
|
|
||||||
|
### Core Dependencies |
||||||
|
- **Realm Database** (10.15.1) - Local data storage |
||||||
|
- **Kotlin Coroutines** - Asynchronous programming |
||||||
|
- **Firebase Crashlytics** - Crash reporting and analytics |
||||||
|
- **Material Design Components** - UI framework |
||||||
|
- **MPAndroidChart** - Data visualization |
||||||
|
- **CameraX** - Image capture functionality |
||||||
|
|
||||||
|
### Testing |
||||||
|
- **JUnit** for unit testing |
||||||
|
- **Android Instrumented Tests** for integration testing |
||||||
|
- Test files located in `app/src/androidTest/` and `app/src/test/` |
||||||
|
|
||||||
|
## Development Guidelines |
||||||
|
|
||||||
|
### Working with Sessions |
||||||
|
- Sessions are the core data model representing individual poker games |
||||||
|
- Use `Session.newInstance()` to create new sessions properly |
||||||
|
- Always call `computeStats()` after modifying session data |
||||||
|
- Sessions can be in various states: PENDING, STARTED, PAUSED, ENDED |
||||||
|
|
||||||
|
### Database Operations |
||||||
|
- Use Realm transactions for data modifications |
||||||
|
- The app uses schema version 14 - increment when making schema changes |
||||||
|
- Migration logic is in `PokerAnalyticsMigration.kt` |
||||||
|
|
||||||
|
### Testing Data |
||||||
|
- Use `FakeDataManager.createFakeSessions()` for generating test data |
||||||
|
- Seed data is available through the `Seed` class |
||||||
|
|
||||||
|
### Build Variants |
||||||
|
- **standard** - Main production flavor |
||||||
|
- Release builds are optimized and obfuscated with ProGuard |
||||||
|
|
||||||
|
## Performance Considerations |
||||||
|
- Sessions use `ComputableResult` for pre-computed statistics |
||||||
|
- Large datasets are handled with Realm's lazy loading |
||||||
|
- Chart rendering is optimized for large data sets |
||||||
|
- Background processing uses Kotlin Coroutines |
||||||
|
|
||||||
|
## Security & Privacy |
||||||
|
- Sensitive data is encrypted in Realm database |
||||||
|
- Crash logging excludes personal information |
||||||
|
- Backup functionality includes data encryption |
||||||
@ -0,0 +1,86 @@ |
|||||||
|
package net.pokeranalytics.android.util |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import androidx.work.Worker |
||||||
|
import androidx.work.WorkerParameters |
||||||
|
import io.realm.Realm |
||||||
|
import kotlinx.coroutines.CoroutineScope |
||||||
|
import kotlinx.coroutines.Dispatchers |
||||||
|
import kotlinx.coroutines.launch |
||||||
|
import net.pokeranalytics.android.api.BackupApi |
||||||
|
import net.pokeranalytics.android.model.realm.Session |
||||||
|
import net.pokeranalytics.android.model.realm.Transaction |
||||||
|
import net.pokeranalytics.android.util.csv.DataType |
||||||
|
import net.pokeranalytics.android.util.csv.ProductCSVDescriptors |
||||||
|
import net.pokeranalytics.android.util.extensions.dateTimeFileFormatted |
||||||
|
import timber.log.Timber |
||||||
|
import java.util.* |
||||||
|
|
||||||
|
|
||||||
|
class BackupWorker(var context: Context, var params: WorkerParameters) : Worker(context, params) { |
||||||
|
|
||||||
|
enum class ParamKeys(val value: String) { |
||||||
|
DATA("title"), |
||||||
|
} |
||||||
|
|
||||||
|
override fun doWork(): Result { |
||||||
|
|
||||||
|
val data = params.inputData |
||||||
|
|
||||||
|
val dataTypeInt = data.getInt(ParamKeys.DATA.value, 0) |
||||||
|
val dataType = DataType.values()[dataTypeInt] |
||||||
|
|
||||||
|
Preferences.getBackupEmail(context)?.let { email -> |
||||||
|
val task = BackupTask(dataType, email, context) |
||||||
|
task.start() |
||||||
|
} |
||||||
|
|
||||||
|
return Result.success() |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class BackupTask(val dataType: DataType, val email: String, val context: Context) { |
||||||
|
|
||||||
|
fun start() { |
||||||
|
when(this.dataType) { |
||||||
|
DataType.SESSION -> { |
||||||
|
backupSessions() |
||||||
|
} |
||||||
|
DataType.TRANSACTION -> { |
||||||
|
backupTransactions() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun backupSessions() { |
||||||
|
|
||||||
|
Timber.d(">>>> backup sessions") |
||||||
|
val realm = Realm.getDefaultInstance() |
||||||
|
val sessions = realm.where(Session::class.java).findAll().sort("startDate") |
||||||
|
val csv = ProductCSVDescriptors.pokerAnalyticsAndroid6Sessions.toCSV(sessions) |
||||||
|
val fileName = "sessions_${Date().dateTimeFileFormatted}.csv" |
||||||
|
|
||||||
|
CoroutineScope(context = Dispatchers.IO).launch { |
||||||
|
val success = BackupApi.backupFile(context, email, fileName, csv) |
||||||
|
Preferences.setSessionsBackupSuccess(success, context) |
||||||
|
} |
||||||
|
realm.close() |
||||||
|
} |
||||||
|
|
||||||
|
private fun backupTransactions() { |
||||||
|
|
||||||
|
Timber.d(">>>> backup transactions") |
||||||
|
val realm = Realm.getDefaultInstance() |
||||||
|
val transactions = realm.where(Transaction::class.java).findAll().sort("date") |
||||||
|
val csv = ProductCSVDescriptors.pokerAnalyticsAndroidTransactions.toCSV(transactions) |
||||||
|
val fileName = "transactions_${Date().dateTimeFileFormatted}.csv" |
||||||
|
|
||||||
|
CoroutineScope(context = Dispatchers.IO).launch { |
||||||
|
val success = BackupApi.backupFile(context, email, fileName, csv) |
||||||
|
Preferences.setTransactionsBackupSuccess(success, context) |
||||||
|
} |
||||||
|
realm.close() |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Binary file not shown.
Loading…
Reference in new issue