docs: Comprehensive README update with new features and architecture details
- Add extensive documentation for offline mode, sync, collections, and dashboard features - Document new Material You theming, OLED mode, and widget capabilities - Include detailed sections on metadata extraction, Markdown editor, and import/export - Update technology stack versions (Kotlin 2.0.0, Hilt 2.51.1, Retrofit 2.11.0, Room 2.6.1) - Simplify installation and compilation instructions - Add user guide with first-time setup and
This commit is contained in:
@@ -18,27 +18,71 @@
|
||||
|
||||
### 📚 Gestion des Favoris
|
||||
- **Flux infini** : Défilement continu avec chargement progressif (Paging 3)
|
||||
- **Recherche côté serveur** : Recherche par termes et filtrage par tags
|
||||
- **Ajout rapide** : Création de liens privés/publics avec description et tags
|
||||
- **Édition** : Modification complète des liens existants
|
||||
- **Suppression** : Gestion facile des favoris
|
||||
- **Recherche avancée** : Recherche locale avec FTS4 (full-text search) et filtrage par tags
|
||||
- **Mode hors-ligne** : Consultation et modification des liens même sans connexion
|
||||
- **Ajout rapide** : Création de liens privés/publics avec description, tags et extraction automatique de métadonnées
|
||||
- **Édition** : Modification complète des liens existants avec éditeur Markdown
|
||||
- **Suppression** : Gestion facile des favoris avec file d'attente de sync
|
||||
- **Détection des doublons** : Alerte lors de l'ajout d'un lien existant avec option de mise à jour
|
||||
- **Liens épinglés** : Mise en avant des liens importants
|
||||
|
||||
### 🏷️ Gestion des Tags
|
||||
- Vue dédiée pour parcourir tous les tags
|
||||
- Compteur d'utilisation par tag
|
||||
- Filtrage rapide du flux par tag
|
||||
- Tags favoris pour accès rapide
|
||||
|
||||
### 📁 Collections
|
||||
- Organisation des liens en collections
|
||||
- Collections intelligentes avec filtres automatiques
|
||||
- Vue grille adaptative pour les collections
|
||||
|
||||
### 📝 Éditeur Markdown
|
||||
- Édition avec prévisualisation en temps réel
|
||||
- Mode édition/prévisualisation/split
|
||||
- Mode lecture focus sans distraction
|
||||
- Barre d'outils de formatage
|
||||
|
||||
### 🌐 Extraction de Métadonnées
|
||||
- Extraction automatique des OpenGraph (titre, description, image)
|
||||
- Détection du type de contenu (article, vidéo, image, audio, code, etc.)
|
||||
- Estimation du temps de lecture
|
||||
- Extraction du nom du site
|
||||
|
||||
### 📊 Tableau de Bord
|
||||
- Statistiques d'utilisation (liens totaux, cette semaine, ce mois)
|
||||
- Temps de lecture total et moyen
|
||||
- Répartition par type de contenu
|
||||
- Tags les plus utilisés
|
||||
- Graphique d'activité sur 30 jours
|
||||
|
||||
### 💾 Import/Export
|
||||
- Export JSON (format complet avec métadonnées)
|
||||
- Export CSV (compatible Excel)
|
||||
- Export HTML (format Netscape/Chrome bookmarks)
|
||||
- Import depuis JSON (export ShaarIt)
|
||||
- Import depuis HTML (bookmarks Chrome/Firefox)
|
||||
|
||||
### 🔄 Synchronisation
|
||||
- Synchronisation automatique en arrière-plan (WorkManager)
|
||||
- Mode offline-first : modifications en attente
|
||||
- Résolution de conflits intelligente
|
||||
- File d'attente des opérations
|
||||
|
||||
### 🔗 Intégration système
|
||||
- **Share Intent Android** : Sauvegarde rapide depuis n'importe quelle app (navigateur, YouTube, etc.) via le menu Partager
|
||||
- **Share Intent Android** : Sauvegarde rapide depuis n'importe quelle app
|
||||
- **App Shortcuts** : Accès rapide via appui long sur l'icône (Ajouter, Aléatoire, Rechercher, Collections)
|
||||
- **Quick Settings Tile** : Tuile pour ajouter rapidement un lien
|
||||
- **Widget** : Widget d'accueil affichant les liens récents
|
||||
- Ouverture des liens dans le navigateur par défaut
|
||||
- Support des URLs partagées avec titre pré-rempli
|
||||
|
||||
### 🎨 Interface Utilisateur
|
||||
- **Design premium** : Thème sombre moderne avec dégradés cyan/bleu
|
||||
- **Material You (Monet)** : Couleurs dynamiques basées sur le fond d'écran (Android 12+)
|
||||
- **Mode OLED** : Noir pur pour les écrans AMOLED
|
||||
- **Design premium** : Thème sombre moderne avec dégradés
|
||||
- **Material Design 3** : Composants UI natifs Android
|
||||
- **Animations fluides** : Transitions et effets visuels
|
||||
- **Deux modes d'affichage** : Liste détaillée ou grille compacte
|
||||
- **Trois modes d'affichage** : Liste détaillée, grille compacte, ou vue compacte
|
||||
- **Pull-to-refresh** : Actualisation du flux par glissement
|
||||
|
||||
---
|
||||
@@ -47,13 +91,15 @@
|
||||
|
||||
| Catégorie | Technologie |
|
||||
|-----------|-------------|
|
||||
| **Langage** | Kotlin 1.9.20 |
|
||||
| **Langage** | Kotlin 2.0.0 |
|
||||
| **UI** | Jetpack Compose + Material Design 3 |
|
||||
| **Architecture** | Clean Architecture + MVVM |
|
||||
| **Injection de dépendances** | Dagger Hilt 2.48.1 |
|
||||
| **Réseau** | Retrofit 2.9.0 + Moshi 1.15.0 + OkHttp 4.12.0 |
|
||||
| **Injection de dépendances** | Dagger Hilt 2.51.1 |
|
||||
| **Réseau** | Retrofit 2.11.0 + Moshi 1.15.1 + OkHttp 4.12.0 |
|
||||
| **Base de données locale** | Room 2.6.1 |
|
||||
| **Pagination** | Paging 3 |
|
||||
| **Concurrence** | Coroutines & Flow |
|
||||
| **Background work** | WorkManager 2.9.0 |
|
||||
| **Stockage sécurisé** | AndroidX Security Crypto |
|
||||
| **Navigation** | Navigation Compose |
|
||||
| **Compilation** | Gradle 8.0+ avec KSP |
|
||||
@@ -79,274 +125,125 @@ Récupérez le dernier APK depuis la section [Releases](../../releases).
|
||||
|
||||
#### Méthode 2 : Compilation depuis les sources
|
||||
|
||||
##### Prérequis de développement
|
||||
1. **JDK 17** (ou plus récent) installé
|
||||
2. **Android SDK** installé (Platform API 34)
|
||||
3. **Gradle** 8.0+ (si `gradlew` est manquant)
|
||||
|
||||
##### Étapes de compilation
|
||||
|
||||
1. **Cloner le repository**
|
||||
```bash
|
||||
git clone https://github.com/votre-username/ShaarIt.git
|
||||
cd ShaarIt
|
||||
```
|
||||
|
||||
2. **Configurer l'emplacement du SDK Android**
|
||||
|
||||
Si la variable `ANDROID_HOME` n'est pas définie, créez un fichier `local.properties` :
|
||||
```bash
|
||||
# Windows
|
||||
echo sdk.dir=C:\Users\<Username>\AppData\Local\Android\Sdk > local.properties
|
||||
|
||||
# Linux/macOS
|
||||
echo sdk.dir=/home/<username>/Android/Sdk > local.properties
|
||||
```
|
||||
|
||||
3. **Compiler l'APK Debug**
|
||||
```bash
|
||||
# Windows
|
||||
./gradlew assembleDebug
|
||||
|
||||
# Linux/macOS
|
||||
chmod +x gradlew
|
||||
./gradlew assembleDebug
|
||||
```
|
||||
|
||||
4. **L'APK se trouve dans** : `app/build/outputs/apk/debug/app-debug.apk`
|
||||
|
||||
5. **Installer sur l'appareil**
|
||||
```bash
|
||||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
```
|
||||
|
||||
### Configuration initiale de l'app
|
||||
|
||||
1. Ouvrez l'application **ShaarIt**
|
||||
2. Entrez l'**URL de votre instance Shaarli** (ex: `https://monserveur.com/shaarli`)
|
||||
3. Entrez votre **Secret API** (trouvé dans les paramètres admin de Shaarli)
|
||||
4. Cliquez sur **Connecter**
|
||||
|
||||
---
|
||||
|
||||
## 🧑💻 Section Développement
|
||||
|
||||
### Architecture du projet
|
||||
|
||||
```
|
||||
app/src/main/java/com/shaarit/
|
||||
├── core/ # Infrastructure et utilitaires
|
||||
│ ├── di/ # Modules Dagger Hilt (injection de dépendances)
|
||||
│ │ ├── AppModule.kt # Fournisseurs d'applications
|
||||
│ │ ├── NetworkModule.kt # Configuration Retrofit/OkHttp
|
||||
│ │ └── RepositoryModule.kt # Liaisons repository
|
||||
│ ├── network/ # Intercepteurs réseau
|
||||
│ │ ├── AuthInterceptor.kt # Injection automatique du token JWT
|
||||
│ │ └── HostSelectionInterceptor.kt # Changement dynamique d'hôte
|
||||
│ ├── storage/ # Stockage local sécurisé
|
||||
│ │ └── TokenManager.kt # Gestion chiffrée des tokens
|
||||
│ └── util/ # Utilitaires
|
||||
│ └── JwtGenerator.kt # Générateur JWT HS512
|
||||
├── data/ # Couche de données (Clean Architecture)
|
||||
│ ├── api/ # Interface Retrofit
|
||||
│ │ └── ShaarliApi.kt # Endpoints API v1
|
||||
│ ├── dto/ # Data Transfer Objects (Moshi)
|
||||
│ │ ├── Dtos.kt # Login, Link, Info DTOs
|
||||
│ │ └── TagDto.kt # Tag DTO
|
||||
│ ├── mapper/ # Convertisseurs DTO ↔ Domain
|
||||
│ │ └── LinkMapper.kt
|
||||
│ ├── paging/ # Sources de pagination
|
||||
│ │ └── LinkPagingSource.kt # Paging 3 pour le flux
|
||||
│ └── repository/ # Implémentations des repositories
|
||||
│ ├── AuthRepositoryImpl.kt
|
||||
│ └── LinkRepositoryImpl.kt
|
||||
├── domain/ # Couche métier (indépendante des frameworks)
|
||||
│ ├── model/ # Modèles de domaine
|
||||
│ │ ├── Models.kt # Credentials, ShaarliLink
|
||||
│ │ ├── ShaarliTag.kt
|
||||
│ │ └── ViewStyle.kt # Enum modes d'affichage
|
||||
│ ├── repository/ # Interfaces de repository
|
||||
│ │ ├── AuthRepository.kt
|
||||
│ │ └── LinkRepository.kt
|
||||
│ └── usecase/ # Cas d'utilisation
|
||||
│ └── LoginUseCase.kt
|
||||
├── presentation/ # Couche présentation (UI)
|
||||
│ ├── auth/ # Écran de connexion
|
||||
│ │ ├── LoginScreen.kt
|
||||
│ │ └── LoginViewModel.kt
|
||||
│ ├── feed/ # Flux principal
|
||||
│ │ ├── FeedScreen.kt
|
||||
│ │ ├── FeedViewModel.kt
|
||||
│ │ └── LinkItemViews.kt # Composants de carte de lien
|
||||
│ ├── add/ # Ajout de lien
|
||||
│ │ ├── AddLinkScreen.kt
|
||||
│ │ └── AddLinkViewModel.kt
|
||||
│ ├── edit/ # Édition de lien
|
||||
│ │ ├── EditLinkScreen.kt
|
||||
│ │ └── EditLinkViewModel.kt
|
||||
│ ├── tags/ # Gestion des tags
|
||||
│ │ ├── TagsScreen.kt
|
||||
│ │ └── TagsViewModel.kt
|
||||
│ └── nav/ # Navigation Compose
|
||||
│ └── NavGraph.kt # Routes et navigation
|
||||
├── ui/ # Composants UI réutilisables
|
||||
│ ├── components/ # Composants custom premium
|
||||
│ │ └── PremiumComponents.kt # GlassCard, GradientButton, etc.
|
||||
│ └── theme/ # Thème Material Design 3
|
||||
│ ├── Theme.kt # Couleurs et thème sombre
|
||||
│ └── Type.kt # Typographie
|
||||
├── MainActivity.kt # Point d'entrée avec gestion Share Intent
|
||||
└── ShaarItApp.kt # Application Hilt
|
||||
```
|
||||
|
||||
### Flux d'authentification JWT
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
|
||||
│ Utilisateur │────▶│ LoginScreen │────▶│ LoginViewModel │
|
||||
└─────────────┘ └──────────────┘ └─────────────────┘
|
||||
│
|
||||
┌───────────────────────────────┘
|
||||
▼
|
||||
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
|
||||
│ TokenManager│◀────│AuthRepository│◀────│ LoginUseCase │
|
||||
└─────────────┘ └──────────────┘ └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────┐
|
||||
│ EncryptedSharedPreferences│ (AES256_GCM)
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
Le token JWT est généré localement avec l'algorithme **HS512** :
|
||||
- **Header** : `{"typ":"JWT","alg":"HS512"}`
|
||||
- **Payload** : `{"iat": <unix_timestamp>}`
|
||||
- **Signature** : HMAC-SHA512(base64url(header) + "." + base64url(payload), apiSecret)
|
||||
|
||||
### Pagination avec Paging 3
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ FeedScreen │────▶│ FeedViewModel │────▶│ LinkRepository │
|
||||
└─────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│
|
||||
┌──────────────────────────────────┘
|
||||
▼
|
||||
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
|
||||
│ lazyPagingItems│◀──│ Pager │◀────│ LinkPagingSource│
|
||||
└─────────────┘ └──────────────┘ └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ ShaarliApi │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
### Configuration réseau dynamique
|
||||
|
||||
L'application utilise un pattern d'intercepteur pour gérer le changement d'URL serveur sans recréer le client Retrofit :
|
||||
|
||||
```kotlin
|
||||
// HostSelectionInterceptor permet de changer l'hôte à la volée
|
||||
class HostSelectionInterceptor : Interceptor {
|
||||
@Volatile private var host: HttpUrl? = null
|
||||
|
||||
fun setHost(url: String) {
|
||||
host = url.toHttpUrlOrNull()
|
||||
}
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
val newUrl = host?.let {
|
||||
request.url.newBuilder()
|
||||
.scheme(it.scheme)
|
||||
.host(it.host)
|
||||
.port(it.port)
|
||||
.build()
|
||||
} ?: request.url
|
||||
|
||||
return chain.proceed(request.newBuilder().url(newUrl).build())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Exécution des tests
|
||||
|
||||
1. Clonez le repository :
|
||||
```bash
|
||||
# Tests unitaires
|
||||
./gradlew test
|
||||
|
||||
# Tests instrumentés
|
||||
./gradlew connectedAndroidTest
|
||||
git clone https://github.com/votre-username/ShaarIt.git
|
||||
cd ShaarIt
|
||||
```
|
||||
|
||||
### Build de release
|
||||
|
||||
1. **Créer un keystore** (si premier build)
|
||||
```bash
|
||||
keytool -genkey -v -keystore shaarit.keystore -alias shaarit \
|
||||
-keyalg RSA -keysize 2048 -validity 10000
|
||||
```
|
||||
|
||||
2. **Créer `keystore.properties`** dans le répertoire racine :
|
||||
```properties
|
||||
storeFile=shaarit.keystore
|
||||
storePassword=votre_password
|
||||
keyAlias=shaarit
|
||||
keyPassword=votre_password
|
||||
```
|
||||
|
||||
3. **Compiler**
|
||||
```bash
|
||||
./gradlew assembleRelease
|
||||
```
|
||||
|
||||
4. **L'APK signé se trouve dans** : `app/build/outputs/apk/release/app-release.apk`
|
||||
|
||||
### Dépendances principales
|
||||
|
||||
```toml
|
||||
[versions]
|
||||
agp = "8.13.2"
|
||||
kotlin = "1.9.20"
|
||||
hilt = "2.48.1"
|
||||
retrofit = "2.9.0"
|
||||
moshi = "1.15.0"
|
||||
okhttp = "4.12.0"
|
||||
paging = "3.2.1"
|
||||
composeBom = "2023.08.00"
|
||||
|
||||
[libraries]
|
||||
# Injection de dépendances
|
||||
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
|
||||
hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
|
||||
|
||||
# Réseau
|
||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||
retrofit-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" }
|
||||
moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
|
||||
moshi-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
|
||||
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
|
||||
|
||||
# Pagination
|
||||
androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging" }
|
||||
androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging" }
|
||||
|
||||
# Sécurité
|
||||
androidx-security-crypto = { module = "androidx.security:security-crypto", version = "1.1.0-alpha06" }
|
||||
2. Compilez l'APK debug :
|
||||
```bash
|
||||
./gradlew assembleDebug
|
||||
```
|
||||
|
||||
### Contribution
|
||||
L'APK sera généré dans `app/build/outputs/apk/debug/`
|
||||
|
||||
1. Forker le projet
|
||||
2. Créer une branche feature (`git checkout -b feature/amazing-feature`)
|
||||
3. Committer vos changements (`git commit -m 'Add amazing feature'`)
|
||||
4. Pusher sur la branche (`git push origin feature/amazing-feature`)
|
||||
5. Ouvrir une Pull Request
|
||||
3. Ou compilez l'APK release (nécessite une configuration de signature) :
|
||||
```bash
|
||||
./gradlew assembleRelease
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📄 Licence
|
||||
## 📖 Guide d'utilisation
|
||||
|
||||
### Première configuration
|
||||
1. Ouvrez l'application
|
||||
2. Entrez l'URL de votre instance Shaarli
|
||||
3. Entrez votre nom d'utilisateur et mot de passe
|
||||
4. L'application générera automatiquement les tokens API nécessaires
|
||||
|
||||
### Ajouter un lien
|
||||
- **Via l'app** : Appuyez sur le bouton + et entrez l'URL
|
||||
- **Via le partage Android** : Partagez n'importe quelle URL vers ShaarIt depuis n'importe quelle app
|
||||
- **Via Quick Settings** : Ajoutez la tuile ShaarIt dans vos paramètres rapides
|
||||
|
||||
### Organiser vos liens
|
||||
- Utilisez les tags pour catégoriser vos liens
|
||||
- Créez des collections pour regrouper des liens par thème
|
||||
- Épinglez les liens importants pour un accès rapide
|
||||
|
||||
### Mode hors-ligne
|
||||
- Tous les liens sont stockés localement dans la base de données Room
|
||||
- Les modifications sont synchronisées automatiquement quand la connexion est disponible
|
||||
- Consultez vos favoris même sans connexion Internet
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
Le projet suit les principes de la **Clean Architecture** avec une séparation claire des couches :
|
||||
|
||||
```
|
||||
├── data/ # Couche de données
|
||||
│ ├── api/ # API Retrofit
|
||||
│ ├── local/ # Base de données Room
|
||||
│ │ ├── dao/ # Data Access Objects
|
||||
│ │ ├── entity/ # Entités Room
|
||||
│ │ └── database/ # Configuration de la DB
|
||||
│ ├── sync/ # Synchronisation
|
||||
│ ├── export/ # Import/Export
|
||||
│ └── repository/ # Implémentations des repositories
|
||||
├── domain/ # Couche domaine
|
||||
│ ├── model/ # Modèles métier
|
||||
│ └── repository/ # Interfaces des repositories
|
||||
├── presentation/ # Couche présentation
|
||||
│ ├── feed/ # Écran principal
|
||||
│ ├── add/ # Ajout de liens
|
||||
│ ├── edit/ # Édition de liens
|
||||
│ ├── tags/ # Gestion des tags
|
||||
│ ├── collections/ # Collections
|
||||
│ ├── dashboard/ # Tableau de bord
|
||||
│ ├── settings/ # Paramètres
|
||||
│ └── nav/ # Navigation
|
||||
└── core/ # Utilitaires
|
||||
├── di/ # Injection de dépendances
|
||||
├── network/ # Configuration réseau
|
||||
└── storage/ # Stockage local
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Roadmap
|
||||
|
||||
- [x] Synchronisation en arrière-plan avec WorkManager
|
||||
- [x] Mode hors-ligne avec Room
|
||||
- [x] Éditeur Markdown
|
||||
- [x] Extraction de métadonnées OpenGraph
|
||||
- [x] Collections d'organisation
|
||||
- [x] Liens épinglés
|
||||
- [x] Widget d'accueil
|
||||
- [x] App Shortcuts
|
||||
- [x] Quick Settings Tile
|
||||
- [x] Tableau de bord analytique
|
||||
- [x] Import/Export
|
||||
- [x] Material You (Monet)
|
||||
- [ ] Recherche avancée avec filtres multiples
|
||||
- [ ] Suggestions de tags par IA
|
||||
- [ ] Mode lecture sans distraction pour les articles
|
||||
- [ ] Partage de collections
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contribution
|
||||
|
||||
Les contributions sont les bienvenues ! N'hésitez pas à :
|
||||
- Ouvrir une issue pour signaler un bug ou suggérer une fonctionnalité
|
||||
- Soumettre une pull request
|
||||
- Améliorer la documentation
|
||||
|
||||
### Signaler un bug
|
||||
1. Vérifiez que le bug n'a pas déjà été signalé
|
||||
2. Ouvrez une issue avec :
|
||||
- Description claire du problème
|
||||
- Étapes pour reproduire
|
||||
- Comportement attendu vs réel
|
||||
- Version Android et de l'app
|
||||
- Logs si disponibles
|
||||
|
||||
---
|
||||
|
||||
## 📝 Licence
|
||||
|
||||
Ce projet est sous licence MIT. Voir le fichier [LICENSE](LICENSE) pour plus de détails.
|
||||
|
||||
@@ -354,12 +251,12 @@ Ce projet est sous licence MIT. Voir le fichier [LICENSE](LICENSE) pour plus de
|
||||
|
||||
## 🙏 Remerciements
|
||||
|
||||
- [Shaarli](https://github.com/shaarli/Shaarli) - Le gestionnaire de favoris auto-hébergé
|
||||
- [Jetpack Compose](https://developer.android.com/jetpack/compose) - UI toolkit moderne Android
|
||||
- [Material Design 3](https://m3.material.io/) - Système de design Google
|
||||
- [Shaarli](https://github.com/shaarli/Shaarli) - Le projet original de gestionnaire de favoris
|
||||
- [Jetpack Compose](https://developer.android.com/jetpack/compose) - Framework UI moderne d'Android
|
||||
- La communauté open source pour les excellentes bibliothèques utilisées
|
||||
|
||||
---
|
||||
|
||||
## 📧 Contact
|
||||
|
||||
Pour toute question ou suggestion, n'hésitez pas à ouvrir une [issue](../../issues).
|
||||
<div align="center">
|
||||
<sub>Fait avec ❤️ pour la communauté Shaarli</sub>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user