Initial upload: iptv-android-tv-hermes

This commit is contained in:
Abderrahim-Mouhcine
2026-08-16 19:57:53 +01:00
commit 534d462553
222 changed files with 22516 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
# Architecture d'iMedia TV+
## Principes fondateurs
- **Clean Architecture** simplifiée : UI → ViewModel → Repository → Data
- **Unidirectional Data Flow (UDF)** : les events montent, les states descendent
- **Single Activity** : une seule `MainActivity`, navigation par écrans Compose
- **Programmation réactive** : Kotlin Coroutines + Flow pour tout ce qui est async
- **Séparation claire** : chaque couche ignore l'existence des couches au-dessus d'elle
## Flux de données complet
```mermaid
sequenceDiagram
actor User as Utilisateur (Télécommande)
participant UI as Compose Screen
participant VM as ViewModel
participant Repo as Repository
participant API as Retrofit / Xtream
participant DB as Room (Cache)
participant Store as EncryptedSP
User->>UI: Appuie sur OK (connexion)
UI->>VM: Event: Login(server, user, pass)
VM->>Repo: authRepository.login(server, user, pass)
Repo->>API: POST /player_api.php?username=...
API-->>Repo: Response { auth: "1", status: "Active" }
Repo->>Store: saveCredentials(encrypted)
Repo-->>VM: Result.Success(userInfo)
VM->>VM: _uiState = UiState.Success
VM-->>UI: StateFlow: HomeScreen data
UI-->>User: Affiche la liste des chaînes
```
## Diagramme de navigation des écrans
```mermaid
stateDiagram-v2
[*] --> SplashScreen
SplashScreen --> LoginScreen : Pas de session
SplashScreen --> HomeScreen : Session valide
LoginScreen --> HomeScreen : Connexion OK
LoginScreen --> LoginScreen : Erreur → reste
HomeScreen --> PlayerScreen : Sélection chaîne Live
HomeScreen --> ProgramGuideScreen : Appui Guide TV
HomeScreen --> ReplayGuideScreen : Appui Replay
HomeScreen --> VodScreen : Appui VOD
HomeScreen --> SeriesScreen : Appui Séries
ProgramGuideScreen --> PlayerScreen : "Regarder"
ReplayGuideScreen --> PlayerScreen : "Revoir"
VodScreen --> PlayerScreen : Lecture film
SeriesScreen --> PlayerScreen : Lecture épisode
PlayerScreen --> HomeScreen : Retour × 2
PlayerScreen --> ProgramGuideScreen : Retour × 1 (si venu du guide)
PlayerScreen --> ReplayGuideScreen : Retour × 1 (si venu du replay)
HomeScreen --> ConfirmQuit : Retour
ConfirmQuit --> [*] : Confirmer
ConfirmQuit --> HomeScreen : Annuler
```
## Organisation des ViewModels
Chaque ViewModel expose :
- Un `StateFlow<UiState<T>>` pour l'état de l'UI
- Des fonctions `onAction(X)` pour les intents utilisateur
- Une vue unique (`viewModelScope`) pour les coroutines
```kotlin
class HomeViewModel(
private val liveRepo: LiveRepository,
private val epgRepo: EpgRepository,
private val favRepo: FavoritesRepository
) : ViewModel() {
private val _uiState = MutableStateFlow<UiState<HomeData>>(UiState.Loading)
val uiState: StateFlow<UiState<HomeData>> = _uiState.asStateFlow()
sealed interface Action {
data class SelectCategory(val id: Int) : Action
data class ToggleFavorite(val streamId: Int) : Action
data class PinChannel(val streamId: Int) : Action
object Refresh : Action
}
fun onAction(action: Action) {
// ...
}
}
```
## Injection de dépendances
Utilisation d'un **manual DI** simple via `AppContainer` (pas de framework lourd comme Hilt/Dagger pour un projet de cette taille).
```kotlin
// AppContainer.kt
class AppContainer(context: Context) {
private val secureStorage = SecureStorage(context)
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor(ErrorInterceptor())
.addInterceptor(AuthInterceptor(secureStorage))
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
private val retrofit = Retrofit.Builder()
.baseUrl("http://placeholder") // overridden per-call
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
val xtreamApi = retrofit.create(XtreamApi::class.java)
// Repositories
val authRepository = AuthRepository(xtreamApi, secureStorage)
val liveRepository = LiveRepository(xtreamApi)
val epgRepository = EpgRepository(xtreamApi, EpgDatabase.getInstance(context))
// ...
}
```
## Décisions d'architecture
| Décision | Alternative écartée | Raison |
|----------|-------------------|--------|
| Single Activity | Multi-Activity | Navigation + état simplifiés |
| Manual DI | Hilt / Koin | Pas de overhead pour projet solo |
| Room pour cache EPG | Simple JSON file | Requêtes typées, ACID, migrations |
| EncryptedSharedPreferences | DataStore | Chiffrement natif, API simple |
| ViewModel + StateFlow | MVVM + LiveData | Flow plus puissant, compose-ready |
| sealed interface pour actions | Callbacks | Testable, traçable, immutable |