# ShaarIt **ShaarIt** est un client Android natif pour [Shaarli](https://github.com/shaarli/Shaarli), le gestionnaire de favoris auto-hébergé. Développé avec les technologies Android modernes, il offre une expérience mobile fluide pour gérer vos liens. ![Tech Stack](https://img.shields.io/badge/Kotlin-7F52FF?style=flat&logo=kotlin&logoColor=white) ![Android](https://img.shields.io/badge/Android-3DDC84?style=flat&logo=android&logoColor=white) ![Jetpack Compose](https://img.shields.io/badge/Jetpack%20Compose-4285F4?style=flat&logo=jetpack-compose&logoColor=white) ![License](https://img.shields.io/badge/License-MIT-yellow.svg) --- ## 📱 Fonctionnalités ### 🔐 Authentification - Connexion sécurisée à votre instance Shaarli auto-hébergée (API v1) - Stockage chiffré des tokens JWT et secrets API via `EncryptedSharedPreferences` - Génération automatique de tokens JWT avec algorithme HS512 ### 📚 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 - **Détection des doublons** : Alerte lors de l'ajout d'un lien existant avec option de mise à jour ### 🏷️ Gestion des Tags - Vue dédiée pour parcourir tous les tags - Compteur d'utilisation par tag - Filtrage rapide du flux par tag ### 🔗 Intégration système - **Share Intent Android** : Sauvegarde rapide depuis n'importe quelle app (navigateur, YouTube, etc.) via le menu Partager - 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 Design 3** : Composants UI natifs Android - **Animations fluides** : Transitions et effets visuels - **Deux modes d'affichage** : Liste détaillée ou grille compacte - **Pull-to-refresh** : Actualisation du flux par glissement --- ## 🛠️ Stack Technique | Catégorie | Technologie | |-----------|-------------| | **Langage** | Kotlin 1.9.20 | | **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 | | **Pagination** | Paging 3 | | **Concurrence** | Coroutines & Flow | | **Stockage sécurisé** | AndroidX Security Crypto | | **Navigation** | Navigation Compose | | **Compilation** | Gradle 8.0+ avec KSP | ### Compatibilité - **minSdk**: 24 (Android 7.0) - **targetSdk**: 34 (Android 14) - **compileSdk**: 34 - **JDK requis**: 17+ --- ## 📥 Installation ### Prérequis utilisateur - Un serveur Shaarli auto-hébergé (v0.12+) avec l'API v1 activée - Un appareil Android 7.0+ ou un émulateur ### Obtenir l'APK #### Méthode 1 : Téléchargement direct 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\\AppData\Local\Android\Sdk > local.properties # Linux/macOS echo sdk.dir=/home//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": }` - **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 ```bash # Tests unitaires ./gradlew test # Tests instrumentés ./gradlew connectedAndroidTest ``` ### 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" } ``` ### Contribution 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 --- ## 📄 Licence Ce projet est sous licence MIT. Voir le fichier [LICENSE](LICENSE) pour plus de détails. --- ## 🙏 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 --- ## 📧 Contact Pour toute question ou suggestion, n'hésitez pas à ouvrir une [issue](../../issues).