diff --git a/.gitignore b/.gitignore index 1ca7e92..609afc2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,7 @@ captures/ keystore.properties *.jks .gitea-token -Cachette-*.apk +# Motif large : un motif au nom du projet a laissé passer 95 Mo lors du renommage. +*.apk update-token.properties +app/src/main/assets/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index cac3c4b..1227899 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,37 @@ # Changelog -Toutes les évolutions notables de Cachette sont documentées ici. +Toutes les évolutions notables de Noxely sont documentées ici. Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le versionnage [SemVer](https://semver.org/lang/fr/). +## [1.6.0] — 2026-09-13 + +### Changé + +- **L'application s'appelle désormais Noxely.** Le nom affiché, l'identifiant d'application + (`fr.tuxgyver.noxely`), le paquet Kotlin, le dépôt et le dossier d'enregistrements + (`Music/Noxely`) suivent tous. + + ⚠️ **Android considère le nouvel identifiant comme une application distincte.** Une + installation en 1.5.0 ne se met donc pas à jour toute seule : il faut la désinstaller, puis + installer Noxely. Les enregistrements déjà rangés dans `Music/Cachette` ne sont pas perdus — + ils sont dans le stockage partagé, restent lisibles par tout lecteur, et le scan continue de + les écarter pour ne pas les afficher en double. + +- Nouvelle icône. Le visuel fourni est détouré de son fond, décliné aux cinq densités et posé + sur une couleur échantillonnée dans l'image : sans cela, le masque du système aurait découpé + une seconde fois un badge qui l'est déjà, en laissant du blanc sur les bords. + +### Ajouté + +- Page « À propos », accessible depuis l'écran Sources : ce que fait l'application, ce qu'elle + garantit, avec quoi elle est construite, ce qui a changé et qui en répond. La version est lue + du manifeste et le journal du CHANGELOG du dépôt, recopié à la compilation — l'un et l'autre + ne peuvent donc pas diverger de la réalité. +- La pastille de licence signale en rouge qu'aucune licence n'est déclarée dans le dépôt, plutôt + que d'en afficher une par défaut. + ## [1.5.0] — 2026-09-13 ### Ajouté @@ -25,7 +52,7 @@ et le versionnage [SemVer](https://semver.org/lang/fr/). ### Modifié -- Les enregistrements sont désormais écrits dans `Music/Cachette` et déclarés dans MediaStore +- Les enregistrements sont désormais écrits dans `Music/Noxely` et déclarés dans MediaStore (Android 10 et plus) : ils deviennent visibles des autres lecteurs et survivent à la désinstallation. Le scan local ignore ce dossier, faute de quoi chaque enregistrement apparaîtrait deux fois. @@ -109,7 +136,7 @@ et le versionnage [SemVer](https://semver.org/lang/fr/). ### Ajouté -- Mise à jour intégrée : Cachette surveille les releases du dépôt, télécharge l'APK en Wi-Fi +- Mise à jour intégrée : Noxely surveille les releases du dépôt, télécharge l'APK en Wi-Fi et propose l'installation. Vérification quotidienne en tâche de fond, et à la demande depuis l'écran Sources, qui affiche aussi la version installée. - La taille approximative de chaque résultat Internet Archive est affichée avant sélection : un diff --git a/Makefile b/Makefile index 6f88367..b519d88 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ DEBUG_APK := app/build/outputs/apk/debug/app-debug.apk # app-release-unsigned.apk) : on liste le répertoire au lieu de deviner. RELEASE_DIR := app/build/outputs/apk/release SIGNED_APK := $(RELEASE_DIR)/app-release.apk -OUTPUT_APK := Cachette-$(VERSION).apk +OUTPUT_APK := Noxely-$(VERSION).apk # --- Publication ----------------------------------------------------------- # Déléguée au script de BorisSkill, qui sait déjà tout faire : résolution du diff --git a/README.md b/README.md index 263c857..ae5b205 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 🎧 Cachette +# 🎧 Noxely > Mettez la musique et les vidéos **de côté** pendant que vous avez du réseau. Écoutez-les quand vous n'en avez plus. @@ -6,10 +6,10 @@ [![Kotlin](https://img.shields.io/badge/Kotlin-2.1.0-7F52FF?logo=kotlin&logoColor=white)](https://kotlinlang.org) [![Compose](https://img.shields.io/badge/Jetpack%20Compose-Material%203-4285F4?logo=jetpackcompose&logoColor=white)](https://developer.android.com/jetpack/compose) [![Media3](https://img.shields.io/badge/Media3-1.11.1-FF6F00?logo=googleplay&logoColor=white)](https://developer.android.com/media/media3) -[![Version](https://img.shields.io/badge/Version-1.5.0-6B4EFF)](VERSION) +[![Version](https://img.shields.io/badge/Version-1.6.0-6B4EFF)](VERSION) [![Hors ligne](https://img.shields.io/badge/Hors%20ligne-100%25-3FD8C0)](#-comment-ça-marche) -Cachette est un lecteur audio et vidéo pensé pour les trajets, les tunnels, les avions et les +Noxely est un lecteur audio et vidéo pensé pour les trajets, les tunnels, les avions et les forfaits épuisés. On choisit ce qu'on emporte, l'application le télécharge, et tout reste lisible sans la moindre connexion. @@ -44,6 +44,7 @@ sans la moindre connexion. | 🔁 **Reprise automatique** | Réseau coupé : mise en attente et reprise. Échec serveur : relance seule après 5 s, 15 s, 45 s | | 📻 **Radio** | Les stations les plus écoutées, en direct — et enregistrables d'un appui | | 🏷️ **Titrage automatique** | L'enregistrement prend le nom du morceau annoncé par la station | +| ℹ️ **Page « À propos »** | Fonctions, garanties, pile technique, journal et auteur | | 👀 **Titre en cours** | Ce que diffuse chaque station, lu dans son flux | | ⏰ **Guet d'un titre** | L'enregistrement se déclenche quand le morceau voulu passe, et s'arrête à sa fin | | 🆕 **Mise à jour intégrée** | Surveille les releases du dépôt, télécharge l'APK en Wi-Fi et propose l'installation | @@ -71,7 +72,7 @@ sans la moindre connexion. | **ccMixter** | La recherche fonctionne, mais les fichiers ne sont servis qu'avec un en-tête `Referer` — que Media3 ne permet pas de poser par téléchargement. | | **Radio Browser** | Flux radio continus : rien à mettre en cache. | -### ⚖️ Ce que Cachette ne fait pas — et pourquoi +### ⚖️ Ce que Noxely ne fait pas — et pourquoi | Service | Pourquoi | |---|---| @@ -194,16 +195,24 @@ sortantes sont celles des URL que vous ajoutez et des catalogues que vous interr --- +> ⚠️ **Changement de nom en 1.6.0.** L'application s'appelait Cachette et son identifiant était +> `fr.tuxgyver.cachette`. Android traite le nouvel identifiant `fr.tuxgyver.noxely` comme une +> application distincte : une installation en 1.5.0 ne se met pas à jour toute seule, il faut la +> désinstaller puis installer Noxely. Les enregistrements déjà rangés dans `Music/Cachette` +> survivent — ils sont dans le stockage partagé — et restent lisibles par tout lecteur. + +--- + ## 📼 Où vont les enregistrements -À partir d'Android 10, dans `Music/Cachette/`, déclarés dans MediaStore : ils sont donc visibles +À partir d'Android 10, dans `Music/Noxely/`, déclarés dans MediaStore : ils sont donc visibles de tous les lecteurs de l'appareil et survivent à la désinstallation. En deçà, ils restent dans le -dossier privé de l'application, MediaStore y réclamant une permission de stockage dont Cachette n'a +dossier privé de l'application, MediaStore y réclamant une permission de stockage dont Noxely n'a pas besoin par ailleurs. Le nom du fichier porte le titre du morceau — et pas seulement la colonne `TITLE` : en publiant le fichier, MediaStore le relit et réécrit ce champ à partir du nom. Le scan local, lui, ignore -`Music/Cachette` pour ne pas afficher deux fois ce que la bibliothèque connaît déjà. +`Music/Noxely` pour ne pas afficher deux fois ce que la bibliothèque connaît déjà. Chaque enregistrement se supprime individuellement depuis l'écran **Hors ligne** : fichier, entrée MediaStore et référence en bibliothèque partent ensemble. « Tout libérer » ne les touche pas. @@ -212,7 +221,7 @@ MediaStore et référence en bibliothèque partent ensemble. « Tout libérer » ## 🆕 Mises à jour -Cachette étant distribuée hors magasin, elle surveille elle-même les releases du dépôt. +Noxely étant distribuée hors magasin, elle surveille elle-même les releases du dépôt. ``` Release publiée ──► vérification quotidienne (WorkManager, Wi-Fi) @@ -245,7 +254,7 @@ giteaUpdateToken=… ``` app/src/main/java/fr/tuxgyver/cachette/ -├── CachetteApp.kt Application + conteneur de dépendances (assemblé à la main) +├── NoxelyApp.kt Application + conteneur de dépendances (assemblé à la main) ├── MainActivity.kt ├── model/ MediaEntry, MediaKind, SourceKind ├── library/ MediaStore, persistance des imports, bibliothèque unifiée diff --git a/VERSION b/VERSION index bc80560..dc1e644 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.5.0 +1.6.0 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f6210e7..5b9ca73 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -8,11 +8,11 @@ plugins { } android { - namespace = "fr.tuxgyver.cachette" + namespace = "fr.tuxgyver.noxely" compileSdk = 36 defaultConfig { - applicationId = "fr.tuxgyver.cachette" + applicationId = "fr.tuxgyver.noxely" minSdk = 26 targetSdk = 35 @@ -33,7 +33,7 @@ android { "" } buildConfigField("String", "GITEA_UPDATE_TOKEN", "\"$updateToken\"") - buildConfigField("String", "UPDATE_API", "\"https://git.tuxtech.fr/api/v1/repos/tuxgyver/Cachette\"") + buildConfigField("String", "UPDATE_API", "\"https://git.tuxtech.fr/api/v1/repos/tuxgyver/Noxely\"") } buildTypes { @@ -77,6 +77,13 @@ android { } } +/** Le journal affiché dans « À propos » est celui du dépôt, pas une copie qui dériverait. */ +val copyChangelog = tasks.register("copyChangelogToAssets") { + from(rootProject.file("CHANGELOG.md")) + into(layout.projectDirectory.dir("src/main/assets")) +} +tasks.named("preBuild") { dependsOn(copyChangelog) } + dependencies { implementation(platform("androidx.compose:compose-bom:2025.10.00")) implementation("androidx.compose.ui:ui") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 346c29c..39ba587 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,7 +18,7 @@ android:maxSdkVersion="32" /> + android:theme="@style/Theme.Noxely"> @@ -50,7 +50,7 @@ diff --git a/app/src/main/java/fr/tuxgyver/cachette/MainActivity.kt b/app/src/main/java/fr/tuxgyver/noxely/MainActivity.kt similarity index 80% rename from app/src/main/java/fr/tuxgyver/cachette/MainActivity.kt rename to app/src/main/java/fr/tuxgyver/noxely/MainActivity.kt index 38e6a0f..19eda3e 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/MainActivity.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/MainActivity.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette +package fr.tuxgyver.noxely import android.Manifest import android.content.pm.PackageManager @@ -9,8 +9,8 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat -import fr.tuxgyver.cachette.ui.CachetteRoot -import fr.tuxgyver.cachette.ui.theme.CachetteTheme +import fr.tuxgyver.noxely.ui.NoxelyRoot +import fr.tuxgyver.noxely.ui.theme.NoxelyTheme class MainActivity : ComponentActivity() { @@ -21,20 +21,20 @@ class MainActivity : ComponentActivity() { enableEdgeToEdge() super.onCreate(savedInstanceState) - val container = (application as CachetteApp).container + val container = (application as NoxelyApp).container container.player.connect() askForNotificationsIfNeeded() setContent { - CachetteTheme { - CachetteRoot(container) + NoxelyTheme { + NoxelyRoot(container) } } } override fun onDestroy() { if (isFinishing) { - (application as CachetteApp).container.player.release() + (application as NoxelyApp).container.player.release() } super.onDestroy() } diff --git a/app/src/main/java/fr/tuxgyver/cachette/CachetteApp.kt b/app/src/main/java/fr/tuxgyver/noxely/NoxelyApp.kt similarity index 65% rename from app/src/main/java/fr/tuxgyver/cachette/CachetteApp.kt rename to app/src/main/java/fr/tuxgyver/noxely/NoxelyApp.kt index 7689e52..ccb14da 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/CachetteApp.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/NoxelyApp.kt @@ -1,25 +1,25 @@ -package fr.tuxgyver.cachette +package fr.tuxgyver.noxely import android.app.Application import android.content.Context -import fr.tuxgyver.cachette.library.ImportedStore -import fr.tuxgyver.cachette.library.LibraryRepository -import fr.tuxgyver.cachette.library.LocalMediaScanner -import fr.tuxgyver.cachette.library.PlaylistStore -import fr.tuxgyver.cachette.offline.DownloadTracker -import fr.tuxgyver.cachette.offline.OfflineModule -import fr.tuxgyver.cachette.playback.PlayerController -import fr.tuxgyver.cachette.radio.RadioDirectory -import fr.tuxgyver.cachette.sources.ArchiveOrgCatalog -import fr.tuxgyver.cachette.sources.Catalog -import fr.tuxgyver.cachette.sources.CatalogId -import fr.tuxgyver.cachette.sources.CatalogItem -import fr.tuxgyver.cachette.sources.WikimediaCatalog -import fr.tuxgyver.cachette.sources.LibriVoxCatalog -import fr.tuxgyver.cachette.sources.RssImporter -import fr.tuxgyver.cachette.sources.UrlImporter -import fr.tuxgyver.cachette.update.UpdateCheckWorker -import fr.tuxgyver.cachette.update.UpdateRepository +import fr.tuxgyver.noxely.library.ImportedStore +import fr.tuxgyver.noxely.library.LibraryRepository +import fr.tuxgyver.noxely.library.LocalMediaScanner +import fr.tuxgyver.noxely.library.PlaylistStore +import fr.tuxgyver.noxely.offline.DownloadTracker +import fr.tuxgyver.noxely.offline.OfflineModule +import fr.tuxgyver.noxely.playback.PlayerController +import fr.tuxgyver.noxely.radio.RadioDirectory +import fr.tuxgyver.noxely.sources.ArchiveOrgCatalog +import fr.tuxgyver.noxely.sources.Catalog +import fr.tuxgyver.noxely.sources.CatalogId +import fr.tuxgyver.noxely.sources.CatalogItem +import fr.tuxgyver.noxely.sources.WikimediaCatalog +import fr.tuxgyver.noxely.sources.LibriVoxCatalog +import fr.tuxgyver.noxely.sources.RssImporter +import fr.tuxgyver.noxely.sources.UrlImporter +import fr.tuxgyver.noxely.update.UpdateCheckWorker +import fr.tuxgyver.noxely.update.UpdateRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -67,7 +67,7 @@ class AppContainer(context: Context) { val updates = UpdateRepository(context, BuildConfig.VERSION_NAME) } -class CachetteApp : Application() { +class NoxelyApp : Application() { lateinit var container: AppContainer private set diff --git a/app/src/main/java/fr/tuxgyver/cachette/library/ImportedStore.kt b/app/src/main/java/fr/tuxgyver/noxely/library/ImportedStore.kt similarity index 96% rename from app/src/main/java/fr/tuxgyver/cachette/library/ImportedStore.kt rename to app/src/main/java/fr/tuxgyver/noxely/library/ImportedStore.kt index 388082a..efb0401 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/library/ImportedStore.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/library/ImportedStore.kt @@ -1,7 +1,7 @@ -package fr.tuxgyver.cachette.library +package fr.tuxgyver.noxely.library import android.content.Context -import fr.tuxgyver.cachette.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaEntry import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/fr/tuxgyver/cachette/library/LibraryRepository.kt b/app/src/main/java/fr/tuxgyver/noxely/library/LibraryRepository.kt similarity index 95% rename from app/src/main/java/fr/tuxgyver/cachette/library/LibraryRepository.kt rename to app/src/main/java/fr/tuxgyver/noxely/library/LibraryRepository.kt index eddbddb..eccb0d4 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/library/LibraryRepository.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/library/LibraryRepository.kt @@ -1,6 +1,6 @@ -package fr.tuxgyver.cachette.library +package fr.tuxgyver.noxely.library -import fr.tuxgyver.cachette.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaEntry import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted diff --git a/app/src/main/java/fr/tuxgyver/cachette/library/LocalMediaScanner.kt b/app/src/main/java/fr/tuxgyver/noxely/library/LocalMediaScanner.kt similarity index 91% rename from app/src/main/java/fr/tuxgyver/cachette/library/LocalMediaScanner.kt rename to app/src/main/java/fr/tuxgyver/noxely/library/LocalMediaScanner.kt index 52148b3..5e0a341 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/library/LocalMediaScanner.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/library/LocalMediaScanner.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.library +package fr.tuxgyver.noxely.library import android.content.ContentUris import android.content.Context @@ -8,9 +8,9 @@ import androidx.core.net.toUri import android.os.Build import android.provider.MediaStore import androidx.core.content.ContextCompat -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.MediaKind -import fr.tuxgyver.cachette.model.SourceKind +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaKind +import fr.tuxgyver.noxely.model.SourceKind import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -124,13 +124,16 @@ class LocalMediaScanner(private val context: Context) { } /** - * Musique et podcasts, hors enregistrements de Cachette : ceux-ci sont déjà décrits par la + * Musique et podcasts, hors enregistrements de Noxely : ceux-ci sont déjà décrits par la * bibliothèque, et les relire ici les afficherait en double. */ private fun audioSelection(): String { val base = "(${MediaStore.Audio.Media.IS_MUSIC} != 0 OR ${MediaStore.Audio.Media.IS_PODCAST} != 0)" return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - "$base AND ${MediaStore.Audio.Media.RELATIVE_PATH} NOT LIKE 'Music/Cachette%'" + // L'ancien dossier reste exclu : les enregistrements d'avant le changement de nom + // sont toujours décrits par la bibliothèque et apparaîtraient sinon en double. + "$base AND ${MediaStore.Audio.Media.RELATIVE_PATH} NOT LIKE 'Music/Noxely%'" + + " AND ${MediaStore.Audio.Media.RELATIVE_PATH} NOT LIKE 'Music/Cachette%'" } else { base } diff --git a/app/src/main/java/fr/tuxgyver/cachette/library/PlaylistStore.kt b/app/src/main/java/fr/tuxgyver/noxely/library/PlaylistStore.kt similarity index 97% rename from app/src/main/java/fr/tuxgyver/cachette/library/PlaylistStore.kt rename to app/src/main/java/fr/tuxgyver/noxely/library/PlaylistStore.kt index 8898ad8..00fbf4b 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/library/PlaylistStore.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/library/PlaylistStore.kt @@ -1,7 +1,7 @@ -package fr.tuxgyver.cachette.library +package fr.tuxgyver.noxely.library import android.content.Context -import fr.tuxgyver.cachette.model.Playlist +import fr.tuxgyver.noxely.model.Playlist import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/fr/tuxgyver/cachette/model/Media.kt b/app/src/main/java/fr/tuxgyver/noxely/model/Media.kt similarity index 97% rename from app/src/main/java/fr/tuxgyver/cachette/model/Media.kt rename to app/src/main/java/fr/tuxgyver/noxely/model/Media.kt index 7e48f5e..7e6515c 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/model/Media.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/model/Media.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.model +package fr.tuxgyver.noxely.model import kotlinx.serialization.Serializable diff --git a/app/src/main/java/fr/tuxgyver/cachette/model/Playlist.kt b/app/src/main/java/fr/tuxgyver/noxely/model/Playlist.kt similarity index 92% rename from app/src/main/java/fr/tuxgyver/cachette/model/Playlist.kt rename to app/src/main/java/fr/tuxgyver/noxely/model/Playlist.kt index 7046b88..47b7236 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/model/Playlist.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/model/Playlist.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.model +package fr.tuxgyver.noxely.model import kotlinx.serialization.Serializable diff --git a/app/src/main/java/fr/tuxgyver/cachette/offline/DownloadTracker.kt b/app/src/main/java/fr/tuxgyver/noxely/offline/DownloadTracker.kt similarity index 93% rename from app/src/main/java/fr/tuxgyver/cachette/offline/DownloadTracker.kt rename to app/src/main/java/fr/tuxgyver/noxely/offline/DownloadTracker.kt index 512aaeb..bf64fd8 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/offline/DownloadTracker.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/offline/DownloadTracker.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.offline +package fr.tuxgyver.noxely.offline import android.content.Context import androidx.annotation.OptIn @@ -8,7 +8,7 @@ import androidx.media3.exoplayer.offline.Download import androidx.media3.exoplayer.offline.DownloadManager import androidx.media3.exoplayer.offline.DownloadRequest import androidx.media3.exoplayer.offline.DownloadService -import fr.tuxgyver.cachette.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaEntry import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -110,7 +110,7 @@ class DownloadTracker( // Ré-ajouter la même requête relance un téléchargement en échec. DownloadService.sendAddDownload( context, - CachetteDownloadService::class.java, + NoxelyDownloadService::class.java, download.request, false, ) @@ -129,15 +129,15 @@ class DownloadTracker( val request = DownloadRequest.Builder(entry.id, entry.uri.toUri()) .setData(entry.title.toByteArray()) .build() - DownloadService.sendAddDownload(context, CachetteDownloadService::class.java, request, false) + DownloadService.sendAddDownload(context, NoxelyDownloadService::class.java, request, false) } fun remove(id: String) { - DownloadService.sendRemoveDownload(context, CachetteDownloadService::class.java, id, false) + DownloadService.sendRemoveDownload(context, NoxelyDownloadService::class.java, id, false) } fun removeAll() { - DownloadService.sendRemoveAllDownloads(context, CachetteDownloadService::class.java, false) + DownloadService.sendRemoveAllDownloads(context, NoxelyDownloadService::class.java, false) } private suspend fun reload() = withContext(Dispatchers.IO) { diff --git a/app/src/main/java/fr/tuxgyver/cachette/offline/CachetteDownloadService.kt b/app/src/main/java/fr/tuxgyver/noxely/offline/NoxelyDownloadService.kt similarity index 87% rename from app/src/main/java/fr/tuxgyver/cachette/offline/CachetteDownloadService.kt rename to app/src/main/java/fr/tuxgyver/noxely/offline/NoxelyDownloadService.kt index 27c495a..ad942b5 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/offline/CachetteDownloadService.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/offline/NoxelyDownloadService.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.offline +package fr.tuxgyver.noxely.offline import android.app.Notification import androidx.annotation.OptIn @@ -9,15 +9,15 @@ import androidx.media3.exoplayer.offline.DownloadService import androidx.media3.exoplayer.scheduler.Scheduler import androidx.media3.exoplayer.workmanager.WorkManagerScheduler import androidx.media3.exoplayer.offline.DownloadNotificationHelper -import fr.tuxgyver.cachette.CachetteApp -import fr.tuxgyver.cachette.R +import fr.tuxgyver.noxely.NoxelyApp +import fr.tuxgyver.noxely.R /** * Service de mise en cache. Il tourne en premier plan pendant les téléchargements et délègue la * reprise après coupure réseau ou redémarrage à WorkManager. */ @OptIn(UnstableApi::class) -class CachetteDownloadService : DownloadService( +class NoxelyDownloadService : DownloadService( NOTIFICATION_ID, DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL, CHANNEL_ID, @@ -29,7 +29,7 @@ class CachetteDownloadService : DownloadService( } override fun getDownloadManager(): DownloadManager = - (application as CachetteApp).container.offline.downloadManager + (application as NoxelyApp).container.offline.downloadManager override fun getScheduler(): Scheduler = WorkManagerScheduler(this, WORK_NAME) diff --git a/app/src/main/java/fr/tuxgyver/cachette/offline/OfflineModule.kt b/app/src/main/java/fr/tuxgyver/noxely/offline/OfflineModule.kt similarity index 96% rename from app/src/main/java/fr/tuxgyver/cachette/offline/OfflineModule.kt rename to app/src/main/java/fr/tuxgyver/noxely/offline/OfflineModule.kt index 2535e76..6808ad5 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/offline/OfflineModule.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/offline/OfflineModule.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.offline +package fr.tuxgyver.noxely.offline import android.content.Context import androidx.annotation.OptIn @@ -31,7 +31,7 @@ class OfflineModule(private val context: Context) { private val httpDataSourceFactory: DataSource.Factory by lazy { DefaultHttpDataSource.Factory() - .setUserAgent("Cachette/1.0 (Android)") + .setUserAgent("Noxely/1.0 (Android)") .setAllowCrossProtocolRedirects(true) .setConnectTimeoutMs(20_000) .setReadTimeoutMs(30_000) diff --git a/app/src/main/java/fr/tuxgyver/cachette/playback/MediaItems.kt b/app/src/main/java/fr/tuxgyver/noxely/playback/MediaItems.kt similarity index 89% rename from app/src/main/java/fr/tuxgyver/cachette/playback/MediaItems.kt rename to app/src/main/java/fr/tuxgyver/noxely/playback/MediaItems.kt index 78051a0..ac966e8 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/playback/MediaItems.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/playback/MediaItems.kt @@ -1,9 +1,9 @@ -package fr.tuxgyver.cachette.playback +package fr.tuxgyver.noxely.playback import androidx.core.net.toUri import androidx.media3.common.MediaItem import androidx.media3.common.MediaMetadata -import fr.tuxgyver.cachette.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaEntry /** * Les `MediaItem` transmis par un contrôleur perdent leur URI en chemin : c'est le service qui la diff --git a/app/src/main/java/fr/tuxgyver/cachette/playback/PlaybackService.kt b/app/src/main/java/fr/tuxgyver/noxely/playback/PlaybackService.kt similarity index 91% rename from app/src/main/java/fr/tuxgyver/cachette/playback/PlaybackService.kt rename to app/src/main/java/fr/tuxgyver/noxely/playback/PlaybackService.kt index bf7925c..fcb377a 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/playback/PlaybackService.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/playback/PlaybackService.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.playback +package fr.tuxgyver.noxely.playback import android.app.PendingIntent import android.content.Intent @@ -13,8 +13,8 @@ import androidx.media3.session.MediaSession import androidx.media3.session.MediaSessionService import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture -import fr.tuxgyver.cachette.CachetteApp -import fr.tuxgyver.cachette.MainActivity +import fr.tuxgyver.noxely.NoxelyApp +import fr.tuxgyver.noxely.MainActivity /** Lecture audio et vidéo en arrière-plan, avec notification et commandes système. */ @OptIn(UnstableApi::class) @@ -24,7 +24,7 @@ class PlaybackService : MediaSessionService() { override fun onCreate() { super.onCreate() - val container = (application as CachetteApp).container + val container = (application as NoxelyApp).container val player = ExoPlayer.Builder(this) .setMediaSourceFactory(DefaultMediaSourceFactory(container.offline.playbackDataSourceFactory())) @@ -76,7 +76,7 @@ class PlaybackService : MediaSessionService() { controller: MediaSession.ControllerInfo, mediaItems: MutableList, ): ListenableFuture> { - val library = (application as CachetteApp).container.library + val library = (application as NoxelyApp).container.library val resolved = mediaItems.mapTo(mutableListOf()) { item -> library.entry(item.mediaId)?.toMediaItem() ?: item } diff --git a/app/src/main/java/fr/tuxgyver/cachette/playback/PlayerController.kt b/app/src/main/java/fr/tuxgyver/noxely/playback/PlayerController.kt similarity index 97% rename from app/src/main/java/fr/tuxgyver/cachette/playback/PlayerController.kt rename to app/src/main/java/fr/tuxgyver/noxely/playback/PlayerController.kt index e84e115..da9f09e 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/playback/PlayerController.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/playback/PlayerController.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.playback +package fr.tuxgyver.noxely.playback import android.content.ComponentName import android.content.Context @@ -6,8 +6,8 @@ import androidx.core.content.ContextCompat import androidx.media3.common.Player import androidx.media3.session.MediaController import androidx.media3.session.SessionToken -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.MediaKind +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaKind import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/fr/tuxgyver/cachette/radio/RadioDirectory.kt b/app/src/main/java/fr/tuxgyver/noxely/radio/RadioDirectory.kt similarity index 98% rename from app/src/main/java/fr/tuxgyver/cachette/radio/RadioDirectory.kt rename to app/src/main/java/fr/tuxgyver/noxely/radio/RadioDirectory.kt index fbd48ca..5c50c8d 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/radio/RadioDirectory.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/radio/RadioDirectory.kt @@ -1,6 +1,6 @@ -package fr.tuxgyver.cachette.radio +package fr.tuxgyver.noxely.radio -import fr.tuxgyver.cachette.sources.openConnection +import fr.tuxgyver.noxely.sources.openConnection import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json diff --git a/app/src/main/java/fr/tuxgyver/cachette/radio/RadioStation.kt b/app/src/main/java/fr/tuxgyver/noxely/radio/RadioStation.kt similarity index 94% rename from app/src/main/java/fr/tuxgyver/cachette/radio/RadioStation.kt rename to app/src/main/java/fr/tuxgyver/noxely/radio/RadioStation.kt index 1132dd4..18d881c 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/radio/RadioStation.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/radio/RadioStation.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.radio +package fr.tuxgyver.noxely.radio /** Une station de radio et son flux direct. */ data class RadioStation( diff --git a/app/src/main/java/fr/tuxgyver/cachette/radio/Recorder.kt b/app/src/main/java/fr/tuxgyver/noxely/radio/Recorder.kt similarity index 98% rename from app/src/main/java/fr/tuxgyver/cachette/radio/Recorder.kt rename to app/src/main/java/fr/tuxgyver/noxely/radio/Recorder.kt index 57ceb8b..81caf8d 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/radio/Recorder.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/radio/Recorder.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.radio +package fr.tuxgyver.noxely.radio import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow diff --git a/app/src/main/java/fr/tuxgyver/cachette/radio/RecordingService.kt b/app/src/main/java/fr/tuxgyver/noxely/radio/RecordingService.kt similarity index 96% rename from app/src/main/java/fr/tuxgyver/cachette/radio/RecordingService.kt rename to app/src/main/java/fr/tuxgyver/noxely/radio/RecordingService.kt index a6226b8..48dc1a9 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/radio/RecordingService.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/radio/RecordingService.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.radio +package fr.tuxgyver.noxely.radio import android.app.Notification import android.app.NotificationChannel @@ -11,13 +11,13 @@ import android.content.pm.ServiceInfo import android.os.Build import android.os.IBinder import androidx.core.app.NotificationCompat -import fr.tuxgyver.cachette.CachetteApp -import fr.tuxgyver.cachette.MainActivity -import fr.tuxgyver.cachette.R -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.MediaKind -import fr.tuxgyver.cachette.model.SourceKind -import fr.tuxgyver.cachette.sources.openConnection +import fr.tuxgyver.noxely.NoxelyApp +import fr.tuxgyver.noxely.MainActivity +import fr.tuxgyver.noxely.R +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaKind +import fr.tuxgyver.noxely.model.SourceKind +import fr.tuxgyver.noxely.sources.openConnection import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -246,7 +246,7 @@ class RecordingService : Service() { val published = output.finish(title, elapsed) if (published != null) { val (uri, size) = published - val container = (application as CachetteApp).container + val container = (application as NoxelyApp).container val entry = MediaEntry( id = "rec:${uri.hashCode()}", source = SourceKind.RECORDING, @@ -343,8 +343,8 @@ class RecordingService : Service() { replace(Regex("[^\\p{L}\\p{N} _-]"), "").trim().replace(' ', '_').take(40).ifBlank { "radio" } companion object { - private const val ACTION_START = "fr.tuxgyver.cachette.RECORD_START" - private const val ACTION_STOP = "fr.tuxgyver.cachette.RECORD_STOP" + private const val ACTION_START = "fr.tuxgyver.noxely.RECORD_START" + private const val ACTION_STOP = "fr.tuxgyver.noxely.RECORD_STOP" private const val EXTRA_ID = "id" private const val EXTRA_NAME = "name" private const val EXTRA_URL = "url" diff --git a/app/src/main/java/fr/tuxgyver/cachette/radio/RecordingSink.kt b/app/src/main/java/fr/tuxgyver/noxely/radio/RecordingSink.kt similarity index 95% rename from app/src/main/java/fr/tuxgyver/cachette/radio/RecordingSink.kt rename to app/src/main/java/fr/tuxgyver/noxely/radio/RecordingSink.kt index 64dcd68..3f67820 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/radio/RecordingSink.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/radio/RecordingSink.kt @@ -1,18 +1,18 @@ -package fr.tuxgyver.cachette.radio +package fr.tuxgyver.noxely.radio import android.content.ContentValues import android.content.Context import android.net.Uri import android.os.Build import android.provider.MediaStore -import fr.tuxgyver.cachette.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaEntry import java.io.File import java.io.OutputStream /** * Destination d'un enregistrement radio. * - * À partir d'Android 10, le fichier est écrit dans `Music/Cachette` via MediaStore : il devient + * À partir d'Android 10, le fichier est écrit dans `Music/Noxely` via MediaStore : il devient * visible de tous les lecteurs de l'appareil et survit à la désinstallation. En deçà, l'écriture * dans MediaStore réclamerait une permission de stockage que l'application n'a pas besoin de * demander par ailleurs, donc le fichier reste dans le dossier privé. @@ -100,7 +100,7 @@ class RecordingSink( replace(Regex("[^\\p{L}\\p{N} ._-]"), "").trim().take(80).ifBlank { "enregistrement" } companion object { - const val RELATIVE_PATH = "Music/Cachette" + const val RELATIVE_PATH = "Music/Noxely" private const val MIN_USEFUL_BYTES = 16 * 1024L /** Supprime un enregistrement, quel que soit l'endroit où il avait été écrit. */ diff --git a/app/src/main/java/fr/tuxgyver/cachette/sources/ArchiveOrgCatalog.kt b/app/src/main/java/fr/tuxgyver/noxely/sources/ArchiveOrgCatalog.kt similarity index 97% rename from app/src/main/java/fr/tuxgyver/cachette/sources/ArchiveOrgCatalog.kt rename to app/src/main/java/fr/tuxgyver/noxely/sources/ArchiveOrgCatalog.kt index 5860759..c3f3671 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/sources/ArchiveOrgCatalog.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/sources/ArchiveOrgCatalog.kt @@ -1,8 +1,8 @@ -package fr.tuxgyver.cachette.sources +package fr.tuxgyver.noxely.sources -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.MediaKind -import fr.tuxgyver.cachette.model.SourceKind +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaKind +import fr.tuxgyver.noxely.model.SourceKind import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json diff --git a/app/src/main/java/fr/tuxgyver/cachette/sources/Catalog.kt b/app/src/main/java/fr/tuxgyver/noxely/sources/Catalog.kt similarity index 97% rename from app/src/main/java/fr/tuxgyver/cachette/sources/Catalog.kt rename to app/src/main/java/fr/tuxgyver/noxely/sources/Catalog.kt index 571679a..fbd970c 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/sources/Catalog.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/sources/Catalog.kt @@ -1,6 +1,6 @@ -package fr.tuxgyver.cachette.sources +package fr.tuxgyver.noxely.sources -import fr.tuxgyver.cachette.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaEntry /** Un résultat de recherche : un album, un livre ou un morceau, pas encore un fichier. */ data class CatalogItem( diff --git a/app/src/main/java/fr/tuxgyver/cachette/sources/Http.kt b/app/src/main/java/fr/tuxgyver/noxely/sources/Http.kt similarity index 86% rename from app/src/main/java/fr/tuxgyver/cachette/sources/Http.kt rename to app/src/main/java/fr/tuxgyver/noxely/sources/Http.kt index 3cccc57..b369d9e 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/sources/Http.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/sources/Http.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.sources +package fr.tuxgyver.noxely.sources import java.net.HttpURLConnection import java.net.URL @@ -19,5 +19,5 @@ internal fun openConnection( instanceFollowRedirects = true connectTimeout = 15_000 readTimeout = readTimeoutMs - setRequestProperty("User-Agent", "Cachette/1.1 (Android)") + setRequestProperty("User-Agent", "Noxely/1.1 (Android)") } diff --git a/app/src/main/java/fr/tuxgyver/cachette/sources/LibriVoxCatalog.kt b/app/src/main/java/fr/tuxgyver/noxely/sources/LibriVoxCatalog.kt similarity index 98% rename from app/src/main/java/fr/tuxgyver/cachette/sources/LibriVoxCatalog.kt rename to app/src/main/java/fr/tuxgyver/noxely/sources/LibriVoxCatalog.kt index ed785a3..e74d42c 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/sources/LibriVoxCatalog.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/sources/LibriVoxCatalog.kt @@ -1,6 +1,6 @@ -package fr.tuxgyver.cachette.sources +package fr.tuxgyver.noxely.sources -import fr.tuxgyver.cachette.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaEntry import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll diff --git a/app/src/main/java/fr/tuxgyver/cachette/sources/RssImporter.kt b/app/src/main/java/fr/tuxgyver/noxely/sources/RssImporter.kt similarity index 96% rename from app/src/main/java/fr/tuxgyver/cachette/sources/RssImporter.kt rename to app/src/main/java/fr/tuxgyver/noxely/sources/RssImporter.kt index 7fcdb15..99e9efa 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/sources/RssImporter.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/sources/RssImporter.kt @@ -1,9 +1,9 @@ -package fr.tuxgyver.cachette.sources +package fr.tuxgyver.noxely.sources import android.util.Xml -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.MediaKind -import fr.tuxgyver.cachette.model.SourceKind +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaKind +import fr.tuxgyver.noxely.model.SourceKind import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.xmlpull.v1.XmlPullParser diff --git a/app/src/main/java/fr/tuxgyver/cachette/sources/UrlImporter.kt b/app/src/main/java/fr/tuxgyver/noxely/sources/UrlImporter.kt similarity index 93% rename from app/src/main/java/fr/tuxgyver/cachette/sources/UrlImporter.kt rename to app/src/main/java/fr/tuxgyver/noxely/sources/UrlImporter.kt index d629c0f..af72181 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/sources/UrlImporter.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/sources/UrlImporter.kt @@ -1,10 +1,10 @@ -package fr.tuxgyver.cachette.sources +package fr.tuxgyver.noxely.sources import android.net.Uri import androidx.core.net.toUri -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.MediaKind -import fr.tuxgyver.cachette.model.SourceKind +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaKind +import fr.tuxgyver.noxely.model.SourceKind import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/app/src/main/java/fr/tuxgyver/cachette/sources/WikimediaCatalog.kt b/app/src/main/java/fr/tuxgyver/noxely/sources/WikimediaCatalog.kt similarity index 95% rename from app/src/main/java/fr/tuxgyver/cachette/sources/WikimediaCatalog.kt rename to app/src/main/java/fr/tuxgyver/noxely/sources/WikimediaCatalog.kt index 9f1853c..62ca71a 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/sources/WikimediaCatalog.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/sources/WikimediaCatalog.kt @@ -1,8 +1,8 @@ -package fr.tuxgyver.cachette.sources +package fr.tuxgyver.noxely.sources -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.MediaKind -import fr.tuxgyver.cachette.model.SourceKind +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaKind +import fr.tuxgyver.noxely.model.SourceKind import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/CachetteRoot.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/NoxelyRoot.kt similarity index 89% rename from app/src/main/java/fr/tuxgyver/cachette/ui/CachetteRoot.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/NoxelyRoot.kt index 58ac547..b133a16 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/CachetteRoot.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/NoxelyRoot.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui +package fr.tuxgyver.noxely.ui import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility @@ -35,22 +35,23 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.SourceKind -import fr.tuxgyver.cachette.sources.CatalogId -import fr.tuxgyver.cachette.ui.browse.CatalogBrowseScreen -import fr.tuxgyver.cachette.ui.browse.CatalogItemScreen -import fr.tuxgyver.cachette.ui.browse.SourceBrowseScreen -import fr.tuxgyver.cachette.ui.library.LibraryScreen -import fr.tuxgyver.cachette.ui.offline.DownloadsScreen -import fr.tuxgyver.cachette.ui.player.MiniPlayer -import fr.tuxgyver.cachette.ui.radio.RadioScreen -import fr.tuxgyver.cachette.ui.playlists.AddToPlaylistSheet -import fr.tuxgyver.cachette.ui.playlists.PlaylistDetailScreen -import fr.tuxgyver.cachette.ui.playlists.PlaylistsScreen -import fr.tuxgyver.cachette.ui.player.NowPlayingScreen -import fr.tuxgyver.cachette.ui.sources.SourcesScreen +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.SourceKind +import fr.tuxgyver.noxely.sources.CatalogId +import fr.tuxgyver.noxely.ui.about.AboutScreen +import fr.tuxgyver.noxely.ui.browse.CatalogBrowseScreen +import fr.tuxgyver.noxely.ui.browse.CatalogItemScreen +import fr.tuxgyver.noxely.ui.browse.SourceBrowseScreen +import fr.tuxgyver.noxely.ui.library.LibraryScreen +import fr.tuxgyver.noxely.ui.offline.DownloadsScreen +import fr.tuxgyver.noxely.ui.player.MiniPlayer +import fr.tuxgyver.noxely.ui.radio.RadioScreen +import fr.tuxgyver.noxely.ui.playlists.AddToPlaylistSheet +import fr.tuxgyver.noxely.ui.playlists.PlaylistDetailScreen +import fr.tuxgyver.noxely.ui.playlists.PlaylistsScreen +import fr.tuxgyver.noxely.ui.player.NowPlayingScreen +import fr.tuxgyver.noxely.ui.sources.SourcesScreen import kotlinx.coroutines.launch private enum class Tab(val route: String, val label: String, val icon: ImageVector) { @@ -65,9 +66,10 @@ private const val CATALOG_ROUTE = "catalog/{catalog}" private const val CATALOG_ITEM_ROUTE = "catalog/{catalog}/item" private const val PLAYLIST_ROUTE = "playlist/{playlist}" private const val RADIO_ROUTE = "radio" +private const val ABOUT_ROUTE = "about" @Composable -fun CachetteRoot(container: AppContainer) { +fun NoxelyRoot(container: AppContainer) { val navController = rememberNavController() val backStackEntry by navController.currentBackStackEntryAsState() val currentRoute = backStackEntry?.destination?.route @@ -145,6 +147,10 @@ fun CachetteRoot(container: AppContainer) { ) } + composable(ABOUT_ROUTE) { + AboutScreen(onBack = navController::popBackStack, contentPadding = padding) + } + composable(RADIO_ROUTE) { RadioScreen( container = container, @@ -174,6 +180,7 @@ fun CachetteRoot(container: AppContainer) { onBrowseSource = { navController.navigate("browse/${it.name}") }, onBrowseCatalog = { navController.navigate("catalog/${it.name}") }, onBrowseRadio = { navController.navigate(RADIO_ROUTE) }, + onOpenAbout = { navController.navigate(ABOUT_ROUTE) }, ) } diff --git a/app/src/main/java/fr/tuxgyver/noxely/ui/about/AboutScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/about/AboutScreen.kt new file mode 100644 index 0000000..4d9844f --- /dev/null +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/about/AboutScreen.kt @@ -0,0 +1,355 @@ +package fr.tuxgyver.noxely.ui.about + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ScrollableTabRow +import androidx.compose.material3.Surface +import androidx.compose.material3.Tab +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.dp +import fr.tuxgyver.noxely.R + +private val TABS = listOf( + "⭐ Fonctions", + "🛡️ Garanties", + "🧱 Technique", + "🕓 Journal", + "👤 Auteur", +) + +/** + * Page « À propos » : ce que fait l'application, ce qu'elle garantit, avec quoi elle est faite, + * ce qui a changé et qui en répond. L'en-tête ne défile pas ; seul l'onglet courant défile. + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AboutScreen(onBack: () -> Unit, contentPadding: PaddingValues) { + var tab by rememberSaveable { mutableIntStateOf(0) } + val listPadding = PaddingValues(bottom = contentPadding.calculateBottomPadding()) + + Column(Modifier.fillMaxSize()) { + TopAppBar( + title = { Text("À propos") }, + navigationIcon = { + IconButton(onClick = onBack) { + Icon(Icons.AutoMirrored.Rounded.ArrowBack, contentDescription = "Retour") + } + }, + ) + + Identity() + + ScrollableTabRow(selectedTabIndex = tab, edgePadding = 16.dp) { + TABS.forEachIndexed { index, label -> + Tab( + selected = tab == index, + onClick = { tab = index }, + text = { Text(label, style = MaterialTheme.typography.labelLarge) }, + ) + } + } + + when (tab) { + 0 -> FeaturesTab(listPadding) + 1 -> GuaranteesTab(listPadding) + 2 -> StackTab(listPadding) + 3 -> ChangelogTab(listPadding) + else -> AuthorTab(listPadding) + } + } +} + +@Composable +private fun Identity() { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) { + Image( + painter = painterResource(R.mipmap.ic_launcher_foreground), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .size(80.dp) + .clip(RoundedCornerShape(20.dp)), + ) + Spacer(Modifier.size(6.dp)) + Text(AppInfo.NAME, style = MaterialTheme.typography.headlineSmall) + Text( + text = AppInfo.TAGLINE, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(Modifier.size(10.dp)) + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Pill("v${AppInfo.VERSION}", MaterialTheme.colorScheme.primary) + // Rouge tant qu'aucune licence n'est déclarée : une garantie qui manque doit se voir. + Pill( + text = AppInfo.LICENSE, + color = if (AppInfo.LICENSE_DEFINED) MaterialTheme.colorScheme.secondary + else MaterialTheme.colorScheme.error, + ) + Pill("Hors ligne", Color(0xFF2E7D32)) + } + Spacer(Modifier.size(12.dp)) + } +} + +@Composable +private fun Pill(text: String, color: Color) { + Surface(color = color.copy(alpha = 0.14f), shape = CircleShape) { + Text( + text = text, + style = MaterialTheme.typography.labelMedium, + color = color, + modifier = Modifier.padding(horizontal = 12.dp, vertical = 5.dp), + ) + } +} + +@Composable +private fun FeaturesTab(padding: PaddingValues) { + LazyColumn( + contentPadding = padding, + verticalArrangement = Arrangement.spacedBy(10.dp), + modifier = Modifier.padding(horizontal = 16.dp), + ) { + items(AppInfo.FEATURES) { (title, description) -> + Column { + Text(title, style = MaterialTheme.typography.titleSmall) + Text( + text = description, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + item { Spacer(Modifier.size(16.dp)) } + } +} + +@Composable +private fun GuaranteesTab(padding: PaddingValues) { + LazyColumn( + contentPadding = padding, + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(horizontal = 16.dp), + ) { + item { + Text( + text = AppInfo.GUARANTEES_INTRO, + style = MaterialTheme.typography.bodyMedium, + ) + Spacer(Modifier.size(4.dp)) + } + items(AppInfo.GUARANTEES) { guarantee -> + Row { + Text("•", color = MaterialTheme.colorScheme.secondary) + Spacer(Modifier.width(10.dp)) + Text( + text = guarantee, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + item { Spacer(Modifier.size(16.dp)) } + } +} + +@Composable +private fun StackTab(padding: PaddingValues) { + LazyColumn( + contentPadding = padding, + verticalArrangement = Arrangement.spacedBy(10.dp), + modifier = Modifier.padding(horizontal = 16.dp), + ) { + items(AppInfo.STACK) { (layer, tools) -> + Row(verticalAlignment = Alignment.Top) { + Text( + text = layer, + style = MaterialTheme.typography.titleSmall, + modifier = Modifier.width(116.dp), + ) + Text( + text = tools, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.weight(1f), + ) + } + } + item { Spacer(Modifier.size(16.dp)) } + } +} + +@Composable +private fun ChangelogTab(padding: PaddingValues) { + val context = LocalContext.current + val lines = remember { readChangelog(context) } + + LazyColumn( + contentPadding = padding, + verticalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier.padding(horizontal = 16.dp), + ) { + if (lines.isEmpty()) { + item { + Text( + text = "Journal indisponible.", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + items(lines) { line -> + when (line) { + is ChangelogLine.Version -> { + Spacer(Modifier.size(8.dp)) + Text( + text = line.text, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.primary, + ) + } + + is ChangelogLine.Section -> Text( + text = line.text, + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.secondary, + ) + + is ChangelogLine.Bullet -> Row { + Text("•", color = MaterialTheme.colorScheme.onSurfaceVariant) + Spacer(Modifier.width(8.dp)) + Text( + text = emphasised(line.text), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } + item { Spacer(Modifier.size(16.dp)) } + } +} + +/** Le fichier n'emploie que du gras : inutile d'embarquer un analyseur Markdown complet. */ +@Composable +private fun emphasised(text: String) = buildAnnotatedString { + var rest = text + while (true) { + val open = rest.indexOf("**") + if (open < 0) break + val close = rest.indexOf("**", open + 2) + if (close < 0) break + append(rest.substring(0, open)) + withStyle(SpanStyle(fontWeight = FontWeight.SemiBold)) { + append(rest.substring(open + 2, close)) + } + rest = rest.substring(close + 2) + } + append(rest) +} + +@Composable +private fun AuthorTab(padding: PaddingValues) { + LazyColumn( + contentPadding = padding, + modifier = Modifier.padding(horizontal = 16.dp), + ) { + item { + Surface( + color = MaterialTheme.colorScheme.surfaceVariant, + shape = MaterialTheme.shapes.large, + modifier = Modifier.fillMaxWidth(), + ) { + Column(Modifier.padding(20.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .size(52.dp) + .clip(CircleShape), + ) { + Surface(color = MaterialTheme.colorScheme.primary, shape = CircleShape) { + Box(Modifier.size(52.dp), contentAlignment = Alignment.Center) { + Text( + text = AppInfo.AUTHOR.take(1).uppercase(), + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.onPrimary, + ) + } + } + } + Spacer(Modifier.width(16.dp)) + Column { + Text(AppInfo.AUTHOR, style = MaterialTheme.typography.titleMedium) + Text( + text = AppInfo.AUTHOR_ROLE, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + + Spacer(Modifier.size(18.dp)) + Text("Dépôt", style = MaterialTheme.typography.labelMedium) + Text( + text = AppInfo.REPOSITORY, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.primary, + ) + + Spacer(Modifier.size(18.dp)) + Text( + text = AppInfo.AUTHORSHIP, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + Spacer(Modifier.size(16.dp)) + } + } +} diff --git a/app/src/main/java/fr/tuxgyver/noxely/ui/about/AppInfo.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/about/AppInfo.kt new file mode 100644 index 0000000..86ec13e --- /dev/null +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/about/AppInfo.kt @@ -0,0 +1,83 @@ +package fr.tuxgyver.noxely.ui.about + +import fr.tuxgyver.noxely.BuildConfig + +/** + * Contenu de la page « À propos », séparé de son rendu. + * + * Tout ce qui est écrit ici se vérifie dans le dépôt : une fonction décrite existe, une garantie + * annoncée est tenue par du code. La version n'est pas recopiée mais lue du manifeste. + */ +object AppInfo { + + const val NAME = "Noxely" + const val TAGLINE = "Mettez la musique de côté pendant que vous avez du réseau." + val VERSION: String get() = BuildConfig.VERSION_NAME + + const val REPOSITORY = "https://git.tuxtech.fr/tuxgyver/Noxely" + const val AUTHOR = "Johnny" + const val AUTHOR_ROLE = "Conception, direction et supervision" + const val AUTHORSHIP = + "Application développée avec l'assistance de l'IA, sous la direction et la supervision de Johnny." + + /** Aucune licence n'est déclarée dans le dépôt : la pastille le signale plutôt que d'en inventer une. */ + const val LICENSE = "Licence à définir" + const val LICENSE_DEFINED = false + + /** Ce que fait l'application, du point de vue de qui l'utilise. */ + val FEATURES = listOf( + "Écouter sans réseau" to + "Ce que vous mettez de côté reste lisible dans le métro, en avion ou forfait épuisé. " + + "Audio et vidéo, avec les commandes au casque et sur l'écran verrouillé.", + "Choisir piste par piste" to + "Un album ou un livre affiche ses pistes avec leur durée et leur taille. Vous cochez " + + "ce que vous emportez, et le bouton annonce le poids exact avant de lancer.", + "Essayer avant de garder" to + "Un appui sur une piste la joue en ligne, sans rien mettre en cache. On décide après.", + "Puiser dans des catalogues libres" to + "Internet Archive, Underground (netlabels et Free Music Archive), LibriVox et " + + "Wikimedia Commons, sans compte ni clé, avec filtrage par langue.", + "Écouter et enregistrer la radio" to + "Les stations les plus écoutées, ce qu'elles diffusent à l'instant, et " + + "l'enregistrement d'un direct — qui prend le nom du morceau annoncé.", + "Guetter un morceau" to + "Vous nommez un titre : l'enregistrement se déclenche dès que la station l'annonce, " + + "et s'arrête à la fin du morceau.", + "Organiser en playlists" to + "Des listes nommées, alimentées d'un appui long, lues à la suite.", + "Rester à jour" to + "L'application surveille les publications du dépôt, télécharge la nouvelle version " + + "en Wi-Fi et propose de l'installer.", + ) + + /** Chaque promesse est tenue par du code, et dit où. */ + val GUARANTEES_INTRO = + "Noxely est un outil personnel : rien n'y transite par un service tiers, et rien ne s'y " + + "télécharge sans un geste de votre part." + + val GUARANTEES = listOf( + "Aucun compte, aucune inscription, aucune télémétrie : le projet n'embarque aucune " + + "bibliothèque d'analyse d'audience.", + "Les seules connexions sortantes sont celles des adresses que vous ajoutez et des " + + "catalogues que vous interrogez.", + "Le cache ne contient que ce que vous avez demandé à garder : rien n'y est écrit pendant " + + "une écoute en ligne, et rien n'en est effacé sans votre geste.", + "Le contenu mis en cache est exclu des sauvegardes dans le nuage et des transferts " + + "d'appareil à appareil.", + "Un téléchargement interrompu par une panne de serveur repart seul, trois fois, en " + + "espaçant les tentatives ; sans réseau, il attend son retour.", + "Les catalogues proposés ne présentent que des œuvres libres de droits ou du domaine " + + "public ; les éléments en accès restreint sont écartés avant même d'être affichés.", + "Le jeton qui sert à vérifier les mises à jour est en lecture seule et ne peut rien publier.", + ) + + /** Couche → outils. Six lignes suffisent. */ + val STACK = listOf( + "Interface" to "Jetpack Compose, Material 3, thèmes clair et sombre", + "Lecture" to "Media3 / ExoPlayer, MediaSessionService, notification système", + "Hors connexion" to "Cache Media3 non évinçant, WorkManager pour la reprise", + "Bibliothèque" to "MediaStore pour l'appareil, fichiers JSON pour les imports", + "Réseau" to "HttpURLConnection, kotlinx.serialization, flux ICY pour la radio", + "Publication" to "APK signé, releases Gitea, mise à jour intégrée", + ) +} diff --git a/app/src/main/java/fr/tuxgyver/noxely/ui/about/Changelog.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/about/Changelog.kt new file mode 100644 index 0000000..afbe8ee --- /dev/null +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/about/Changelog.kt @@ -0,0 +1,53 @@ +package fr.tuxgyver.noxely.ui.about + +import android.content.Context + +/** Une ligne du journal, telle qu'elle sera rendue. */ +sealed interface ChangelogLine { + data class Version(val text: String) : ChangelogLine + data class Section(val text: String) : ChangelogLine + data class Bullet(val text: String) : ChangelogLine +} + +/** + * Lit le CHANGELOG livré dans les ressources. + * + * Le fichier est celui du dépôt, recopié dans les assets à la compilation : le journal affiché + * ne peut donc pas diverger de celui que la publication utilise pour ses notes de version. + * Seules les formes réellement employées par le fichier sont interprétées — inutile d'embarquer + * un analyseur Markdown complet pour trois motifs. Le préambule qui précède la première version + * est ignoré : il décrit la convention du fichier, pas l'histoire du logiciel. + */ +fun readChangelog(context: Context, limitVersions: Int = 6): List { + val raw = runCatching { + context.assets.open("CHANGELOG.md").bufferedReader().readText() + }.getOrNull() ?: return emptyList() + + val lines = mutableListOf() + var versions = 0 + + for (line in raw.lineSequence()) { + val trimmed = line.trim() + when { + trimmed.startsWith("## ") -> { + versions++ + if (versions > limitVersions) break + lines += ChangelogLine.Version(trimmed.removePrefix("## ").replace("[", "").replace("]", "")) + } + + trimmed.startsWith("### ") -> + lines += ChangelogLine.Section(trimmed.removePrefix("### ")) + + trimmed.startsWith("- ") && versions > 0 -> + lines += ChangelogLine.Bullet(trimmed.removePrefix("- ")) + + // Une puce peut courir sur plusieurs lignes : on la recolle à la précédente. + trimmed.isNotEmpty() && versions > 0 && lines.lastOrNull() is ChangelogLine.Bullet -> { + val previous = lines.removeAt(lines.lastIndex) as ChangelogLine.Bullet + lines += ChangelogLine.Bullet("${previous.text} $trimmed") + } + + } + } + return lines +} diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/browse/CatalogBrowseScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/browse/CatalogBrowseScreen.kt similarity index 96% rename from app/src/main/java/fr/tuxgyver/cachette/ui/browse/CatalogBrowseScreen.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/browse/CatalogBrowseScreen.kt index 3daef04..a65c379 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/browse/CatalogBrowseScreen.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/browse/CatalogBrowseScreen.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.browse +package fr.tuxgyver.noxely.ui.browse import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll @@ -47,12 +47,12 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.model.MediaKind -import fr.tuxgyver.cachette.sources.CatalogId -import fr.tuxgyver.cachette.sources.CatalogItem -import fr.tuxgyver.cachette.ui.components.Artwork -import fr.tuxgyver.cachette.ui.components.formatBytes +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.model.MediaKind +import fr.tuxgyver.noxely.sources.CatalogId +import fr.tuxgyver.noxely.sources.CatalogItem +import fr.tuxgyver.noxely.ui.components.Artwork +import fr.tuxgyver.noxely.ui.components.formatBytes import kotlinx.coroutines.launch /** diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/browse/CatalogItemScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/browse/CatalogItemScreen.kt similarity index 97% rename from app/src/main/java/fr/tuxgyver/cachette/ui/browse/CatalogItemScreen.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/browse/CatalogItemScreen.kt index 9bf3c6b..a2a71e0 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/browse/CatalogItemScreen.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/browse/CatalogItemScreen.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.browse +package fr.tuxgyver.noxely.ui.browse import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -40,11 +40,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.sources.CatalogId -import fr.tuxgyver.cachette.ui.components.formatBytes -import fr.tuxgyver.cachette.ui.components.formatDuration +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.sources.CatalogId +import fr.tuxgyver.noxely.ui.components.formatBytes +import fr.tuxgyver.noxely.ui.components.formatDuration import kotlinx.coroutines.launch /** diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/browse/SelectionBar.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/browse/SelectionBar.kt similarity index 98% rename from app/src/main/java/fr/tuxgyver/cachette/ui/browse/SelectionBar.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/browse/SelectionBar.kt index 2cb0f72..38a8630 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/browse/SelectionBar.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/browse/SelectionBar.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.browse +package fr.tuxgyver.noxely.ui.browse import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.slideInVertically diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/browse/SourceBrowseScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/browse/SourceBrowseScreen.kt similarity index 97% rename from app/src/main/java/fr/tuxgyver/cachette/ui/browse/SourceBrowseScreen.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/browse/SourceBrowseScreen.kt index b6a86f7..669f129 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/browse/SourceBrowseScreen.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/browse/SourceBrowseScreen.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.browse +package fr.tuxgyver.noxely.ui.browse import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement @@ -35,10 +35,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.SourceKind -import fr.tuxgyver.cachette.ui.components.MediaRow +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.SourceKind +import fr.tuxgyver.noxely.ui.components.MediaRow /** * Parcours d'une source déjà présente dans la bibliothèque : on y choisit, un par un ou en lot, diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/components/Artwork.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/components/Artwork.kt similarity index 97% rename from app/src/main/java/fr/tuxgyver/cachette/ui/components/Artwork.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/components/Artwork.kt index fdea96a..b806df9 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/components/Artwork.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/components/Artwork.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.components +package fr.tuxgyver.noxely.ui.components import android.graphics.Bitmap import android.os.Build @@ -28,7 +28,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.core.net.toUri import coil.compose.AsyncImage -import fr.tuxgyver.cachette.model.MediaKind +import fr.tuxgyver.noxely.model.MediaKind import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/components/Format.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/components/Format.kt similarity index 94% rename from app/src/main/java/fr/tuxgyver/cachette/ui/components/Format.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/components/Format.kt index 430ea5d..1799aa3 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/components/Format.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/components/Format.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.components +package fr.tuxgyver.noxely.ui.components import java.util.Locale diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/components/MediaRow.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/components/MediaRow.kt similarity index 98% rename from app/src/main/java/fr/tuxgyver/cachette/ui/components/MediaRow.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/components/MediaRow.kt index 46daf77..ce20d0b 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/components/MediaRow.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/components/MediaRow.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.components +package fr.tuxgyver.noxely.ui.components import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.ExperimentalFoundationApi @@ -32,8 +32,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.offline.OfflineState +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.offline.OfflineState @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/library/LibraryScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/library/LibraryScreen.kt similarity index 95% rename from app/src/main/java/fr/tuxgyver/cachette/ui/library/LibraryScreen.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/library/LibraryScreen.kt index 67fbc27..f0454f1 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/library/LibraryScreen.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/library/LibraryScreen.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.library +package fr.tuxgyver.noxely.ui.library import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -40,12 +40,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.library.LocalMediaScanner -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.MediaKind -import fr.tuxgyver.cachette.offline.OfflineState -import fr.tuxgyver.cachette.ui.components.MediaRow +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.library.LocalMediaScanner +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaKind +import fr.tuxgyver.noxely.offline.OfflineState +import fr.tuxgyver.noxely.ui.components.MediaRow private enum class LibraryFilter(val label: String) { ALL("Tout"), @@ -201,7 +201,7 @@ private fun PermissionCard(onGrant: () -> Unit) { Text("Accéder à vos médias", style = MaterialTheme.typography.titleMedium) Spacer(Modifier.size(6.dp)) Text( - "Cachette lit la musique et les vidéos déjà présentes sur l'appareil. Rien n'est envoyé ailleurs.", + "Noxely lit la musique et les vidéos déjà présentes sur l'appareil. Rien n'est envoyé ailleurs.", style = MaterialTheme.typography.bodyMedium, ) Spacer(Modifier.size(14.dp)) diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/offline/DownloadsScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/offline/DownloadsScreen.kt similarity index 96% rename from app/src/main/java/fr/tuxgyver/cachette/ui/offline/DownloadsScreen.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/offline/DownloadsScreen.kt index c73a047..320ddbc 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/offline/DownloadsScreen.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/offline/DownloadsScreen.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.offline +package fr.tuxgyver.noxely.ui.offline import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -37,12 +37,12 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.SourceKind -import fr.tuxgyver.cachette.radio.RecordingSink -import fr.tuxgyver.cachette.ui.components.MediaRow -import fr.tuxgyver.cachette.ui.components.formatBytes +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.SourceKind +import fr.tuxgyver.noxely.radio.RecordingSink +import fr.tuxgyver.noxely.ui.components.MediaRow +import fr.tuxgyver.noxely.ui.components.formatBytes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/player/MiniPlayer.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/player/MiniPlayer.kt similarity index 96% rename from app/src/main/java/fr/tuxgyver/cachette/ui/player/MiniPlayer.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/player/MiniPlayer.kt index d4f5816..cdd0790 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/player/MiniPlayer.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/player/MiniPlayer.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.player +package fr.tuxgyver.noxely.ui.player import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.clickable @@ -26,8 +26,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import fr.tuxgyver.cachette.playback.PlayerUiState -import fr.tuxgyver.cachette.ui.components.Artwork +import fr.tuxgyver.noxely.playback.PlayerUiState +import fr.tuxgyver.noxely.ui.components.Artwork @Composable fun MiniPlayer( diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/player/NowPlayingScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/player/NowPlayingScreen.kt similarity index 97% rename from app/src/main/java/fr/tuxgyver/cachette/ui/player/NowPlayingScreen.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/player/NowPlayingScreen.kt index 523d23f..b8d3cb8 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/player/NowPlayingScreen.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/player/NowPlayingScreen.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.player +package fr.tuxgyver.noxely.ui.player import androidx.annotation.OptIn import androidx.compose.foundation.background @@ -54,12 +54,12 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.media3.common.Player import androidx.media3.common.util.UnstableApi import androidx.media3.ui.PlayerView -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.MediaKind -import fr.tuxgyver.cachette.offline.OfflineState -import fr.tuxgyver.cachette.playback.PlayerUiState -import fr.tuxgyver.cachette.ui.components.Artwork -import fr.tuxgyver.cachette.ui.components.formatDuration +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaKind +import fr.tuxgyver.noxely.offline.OfflineState +import fr.tuxgyver.noxely.playback.PlayerUiState +import fr.tuxgyver.noxely.ui.components.Artwork +import fr.tuxgyver.noxely.ui.components.formatDuration @Composable fun NowPlayingScreen( diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/playlists/AddToPlaylistSheet.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/playlists/AddToPlaylistSheet.kt similarity index 97% rename from app/src/main/java/fr/tuxgyver/cachette/ui/playlists/AddToPlaylistSheet.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/playlists/AddToPlaylistSheet.kt index c3f531f..64c0b23 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/playlists/AddToPlaylistSheet.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/playlists/AddToPlaylistSheet.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.playlists +package fr.tuxgyver.noxely.ui.playlists import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -35,8 +35,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.model.MediaEntry +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.model.MediaEntry import kotlinx.coroutines.launch /** Choix de la playlist d'accueil, avec création à la volée si aucune ne convient. */ diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/playlists/PlaylistDetailScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/playlists/PlaylistDetailScreen.kt similarity index 96% rename from app/src/main/java/fr/tuxgyver/cachette/ui/playlists/PlaylistDetailScreen.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/playlists/PlaylistDetailScreen.kt index f54c51e..3eee17b 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/playlists/PlaylistDetailScreen.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/playlists/PlaylistDetailScreen.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.playlists +package fr.tuxgyver.noxely.ui.playlists import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -33,9 +33,9 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.ui.components.MediaRow +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.ui.components.MediaRow import kotlinx.coroutines.launch /** Le contenu d'une playlist, dans l'ordre d'ajout. */ diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/playlists/PlaylistsScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/playlists/PlaylistsScreen.kt similarity index 98% rename from app/src/main/java/fr/tuxgyver/cachette/ui/playlists/PlaylistsScreen.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/playlists/PlaylistsScreen.kt index 30e5475..65bd338 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/playlists/PlaylistsScreen.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/playlists/PlaylistsScreen.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.playlists +package fr.tuxgyver.noxely.ui.playlists import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -39,7 +39,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import fr.tuxgyver.cachette.AppContainer +import fr.tuxgyver.noxely.AppContainer import kotlinx.coroutines.launch @Composable diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/radio/RadioScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/radio/RadioScreen.kt similarity index 95% rename from app/src/main/java/fr/tuxgyver/cachette/ui/radio/RadioScreen.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/radio/RadioScreen.kt index 7e16036..95945fc 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/radio/RadioScreen.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/radio/RadioScreen.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.radio +package fr.tuxgyver.noxely.ui.radio import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll @@ -57,17 +57,17 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.model.MediaEntry -import fr.tuxgyver.cachette.model.MediaKind -import fr.tuxgyver.cachette.model.SourceKind -import fr.tuxgyver.cachette.radio.RADIO_GENRES -import fr.tuxgyver.cachette.radio.RadioStation -import fr.tuxgyver.cachette.radio.Recorder -import fr.tuxgyver.cachette.radio.RecordingService -import fr.tuxgyver.cachette.ui.components.Artwork -import fr.tuxgyver.cachette.ui.components.formatBytes -import fr.tuxgyver.cachette.ui.components.formatDuration +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.model.MediaEntry +import fr.tuxgyver.noxely.model.MediaKind +import fr.tuxgyver.noxely.model.SourceKind +import fr.tuxgyver.noxely.radio.RADIO_GENRES +import fr.tuxgyver.noxely.radio.RadioStation +import fr.tuxgyver.noxely.radio.Recorder +import fr.tuxgyver.noxely.radio.RecordingService +import fr.tuxgyver.noxely.ui.components.Artwork +import fr.tuxgyver.noxely.ui.components.formatBytes +import fr.tuxgyver.noxely.ui.components.formatDuration import kotlinx.coroutines.delay import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -258,7 +258,7 @@ fun RadioScreen( onDismiss = { scheduleFor = null }, onConfirm = { wanted -> RecordingService.start(context, station, whenTitleContains = wanted) - onMessage("Cachette guette « $wanted » sur ${station.name}") + onMessage("Noxely guette « $wanted » sur ${station.name}") scheduleFor = null }, ) @@ -293,7 +293,7 @@ private fun ScheduleDialog( text = { Column { Text( - text = "Cachette écoutera ${station.name} et déclenchera l'enregistrement dès " + + text = "Noxely écoutera ${station.name} et déclenchera l'enregistrement dès " + "qu'un titre contenant ce texte sera annoncé, puis s'arrêtera à la fin du morceau.", style = MaterialTheme.typography.bodyMedium, ) diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/sources/SourcesScreen.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/sources/SourcesScreen.kt similarity index 95% rename from app/src/main/java/fr/tuxgyver/cachette/ui/sources/SourcesScreen.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/sources/SourcesScreen.kt index 4da533b..fc1fdb3 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/sources/SourcesScreen.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/sources/SourcesScreen.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.sources +package fr.tuxgyver.noxely.ui.sources import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -17,6 +17,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight import androidx.compose.material.icons.automirrored.rounded.MenuBook import androidx.compose.material.icons.rounded.Headphones +import androidx.compose.material.icons.rounded.Info import androidx.compose.material.icons.rounded.LibraryMusic import androidx.compose.material.icons.rounded.Public import androidx.compose.material.icons.rounded.Radio @@ -49,9 +50,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.model.SourceKind -import fr.tuxgyver.cachette.sources.CatalogId +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.model.SourceKind +import fr.tuxgyver.noxely.sources.CatalogId import kotlinx.coroutines.launch @Composable @@ -62,6 +63,7 @@ fun SourcesScreen( onBrowseSource: (SourceKind) -> Unit, onBrowseCatalog: (CatalogId) -> Unit, onBrowseRadio: () -> Unit, + onOpenAbout: () -> Unit, ) { val entries by container.library.entries.collectAsStateWithLifecycle() var sheet by remember { mutableStateOf(null) } @@ -176,6 +178,18 @@ fun SourcesScreen( UpdateCard(container) } + item { + SourceCard( + icon = Icons.Rounded.Info, + title = "À propos", + status = "Version ${fr.tuxgyver.noxely.BuildConfig.VERSION_NAME}", + description = "Ce que fait Noxely, ce qu'elle garantit, avec quoi elle est faite et qui en répond.", + actionLabel = "Ouvrir", + onAction = onOpenAbout, + onOpen = onOpenAbout, + ) + } + unavailableSourceCards( listOf( UnavailableSource( diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/sources/UpdateCard.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/sources/UpdateCard.kt similarity index 94% rename from app/src/main/java/fr/tuxgyver/cachette/ui/sources/UpdateCard.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/sources/UpdateCard.kt index ed4da84..90dc2e6 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/sources/UpdateCard.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/sources/UpdateCard.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.sources +package fr.tuxgyver.noxely.ui.sources import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -24,10 +24,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import fr.tuxgyver.cachette.AppContainer -import fr.tuxgyver.cachette.BuildConfig -import fr.tuxgyver.cachette.update.UpdateInfo -import fr.tuxgyver.cachette.update.UpdateNotifier +import fr.tuxgyver.noxely.AppContainer +import fr.tuxgyver.noxely.BuildConfig +import fr.tuxgyver.noxely.update.UpdateInfo +import fr.tuxgyver.noxely.update.UpdateNotifier import kotlinx.coroutines.launch import java.io.File @@ -82,7 +82,7 @@ fun UpdateCard(container: AppContainer) { Text( text = when (current) { UpdateUiState.Idle -> - "Cachette regarde chaque jour si une nouvelle version a été publiée, et la télécharge en Wi-Fi." + "Noxely regarde chaque jour si une nouvelle version a été publiée, et la télécharge en Wi-Fi." UpdateUiState.Checking -> "Vérification en cours…" UpdateUiState.UpToDate -> "Vous avez déjà la dernière version." is UpdateUiState.Available -> "Version ${current.info.version} disponible." diff --git a/app/src/main/java/fr/tuxgyver/cachette/ui/theme/Theme.kt b/app/src/main/java/fr/tuxgyver/noxely/ui/theme/Theme.kt similarity index 93% rename from app/src/main/java/fr/tuxgyver/cachette/ui/theme/Theme.kt rename to app/src/main/java/fr/tuxgyver/noxely/ui/theme/Theme.kt index 1b31e55..9392df7 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/ui/theme/Theme.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/ui/theme/Theme.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.ui.theme +package fr.tuxgyver.noxely.ui.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.shape.RoundedCornerShape @@ -57,7 +57,7 @@ private val DarkColors = darkColorScheme( outlineVariant = Color(0xFF3A3048), ) -private val CachetteShapes = Shapes( +private val NoxelyShapes = Shapes( extraSmall = RoundedCornerShape(8.dp), small = RoundedCornerShape(14.dp), medium = RoundedCornerShape(20.dp), @@ -65,7 +65,7 @@ private val CachetteShapes = Shapes( extraLarge = RoundedCornerShape(36.dp), ) -private val CachetteTypography = Typography().run { +private val NoxelyTypography = Typography().run { copy( displaySmall = displaySmall.copy(fontWeight = FontWeight.Bold, letterSpacing = (-0.5).sp), headlineMedium = headlineMedium.copy(fontWeight = FontWeight.Bold, letterSpacing = (-0.4).sp), @@ -77,7 +77,7 @@ private val CachetteTypography = Typography().run { } @Composable -fun CachetteTheme( +fun NoxelyTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit, ) { @@ -87,8 +87,8 @@ fun CachetteTheme( val colors = if (darkTheme) DarkColors else LightColors MaterialTheme( colorScheme = colors, - shapes = CachetteShapes, - typography = CachetteTypography, + shapes = NoxelyShapes, + typography = NoxelyTypography, content = content, ) } diff --git a/app/src/main/java/fr/tuxgyver/cachette/update/UpdateCheckWorker.kt b/app/src/main/java/fr/tuxgyver/noxely/update/UpdateCheckWorker.kt similarity index 92% rename from app/src/main/java/fr/tuxgyver/cachette/update/UpdateCheckWorker.kt rename to app/src/main/java/fr/tuxgyver/noxely/update/UpdateCheckWorker.kt index f476778..d01728e 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/update/UpdateCheckWorker.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/update/UpdateCheckWorker.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.update +package fr.tuxgyver.noxely.update import android.content.Context import androidx.work.Constraints @@ -8,7 +8,7 @@ import androidx.work.NetworkType import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkerParameters -import fr.tuxgyver.cachette.CachetteApp +import fr.tuxgyver.noxely.NoxelyApp import java.util.concurrent.TimeUnit /** @@ -21,7 +21,7 @@ class UpdateCheckWorker( ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { - val repository = (applicationContext as CachetteApp).container.updates + val repository = (applicationContext as NoxelyApp).container.updates val update = runCatching { repository.checkForUpdate() }.getOrNull() ?: return Result.success() val apk = runCatching { repository.downloadApk(update) }.getOrNull() diff --git a/app/src/main/java/fr/tuxgyver/cachette/update/UpdateNotifier.kt b/app/src/main/java/fr/tuxgyver/noxely/update/UpdateNotifier.kt similarity index 95% rename from app/src/main/java/fr/tuxgyver/cachette/update/UpdateNotifier.kt rename to app/src/main/java/fr/tuxgyver/noxely/update/UpdateNotifier.kt index 0dcd340..215a3e4 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/update/UpdateNotifier.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/update/UpdateNotifier.kt @@ -1,4 +1,4 @@ -package fr.tuxgyver.cachette.update +package fr.tuxgyver.noxely.update import android.Manifest import android.app.NotificationChannel @@ -12,7 +12,7 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat import androidx.core.content.FileProvider -import fr.tuxgyver.cachette.R +import fr.tuxgyver.noxely.R import java.io.File /** Prévient que la mise à jour est téléchargée et ouvre l'installateur système d'un appui. */ @@ -38,7 +38,7 @@ object UpdateNotifier { val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_offline) - .setContentTitle("Cachette $version est prête") + .setContentTitle("Noxely $version est prête") .setContentText("Appuyez pour installer la mise à jour.") .setContentIntent(pending) .setAutoCancel(true) @@ -73,7 +73,7 @@ object UpdateNotifier { CHANNEL_ID, "Mises à jour", NotificationManager.IMPORTANCE_DEFAULT, - ).apply { description = "Disponibilité d'une nouvelle version de Cachette" } + ).apply { description = "Disponibilité d'une nouvelle version de Noxely" } context.getSystemService(NotificationManager::class.java).createNotificationChannel(channel) } } diff --git a/app/src/main/java/fr/tuxgyver/cachette/update/UpdateRepository.kt b/app/src/main/java/fr/tuxgyver/noxely/update/UpdateRepository.kt similarity index 96% rename from app/src/main/java/fr/tuxgyver/cachette/update/UpdateRepository.kt rename to app/src/main/java/fr/tuxgyver/noxely/update/UpdateRepository.kt index 06a5457..9c482a9 100644 --- a/app/src/main/java/fr/tuxgyver/cachette/update/UpdateRepository.kt +++ b/app/src/main/java/fr/tuxgyver/noxely/update/UpdateRepository.kt @@ -1,7 +1,7 @@ -package fr.tuxgyver.cachette.update +package fr.tuxgyver.noxely.update import android.content.Context -import fr.tuxgyver.cachette.BuildConfig +import fr.tuxgyver.noxely.BuildConfig import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json @@ -103,7 +103,7 @@ class UpdateRepository( instanceFollowRedirects = true connectTimeout = 15_000 readTimeout = 60_000 - setRequestProperty("User-Agent", "Cachette/$currentVersion (Android)") + setRequestProperty("User-Agent", "Noxely/$currentVersion (Android)") setRequestProperty("Authorization", "token ${BuildConfig.GITEA_UPDATE_TOKEN}") } diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index 3e8179d..0000000 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml index 5c84730..be43858 100644 --- a/app/src/main/res/mipmap-anydpi/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -1,6 +1,5 @@ - - + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..27d5c62 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..817dd52 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..7db92db Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..48b1202 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..c4d2755 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 341e829..f0e9128 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,5 +1,5 @@ -