Catégories
Actualités Scolaires

Comment Grammarly a construit un clavier natif pour Android

L'application Grammarly sur Android et iOS est un clavier natif. La motivation derrière la création d'un clavier, par opposition à une application mobile traditionnelle comme un éditeur de texte, est le même principe derrière l'extension de navigateur de Grammarly: l'assistant d'écriture doit être disponible partout où l'utilisateur écrit. Le clavier grammatical sur Android est utilisé plus de 70 fois par jour par l'utilisateur moyen, et il a un impact significatif sur la façon dont les gens communiquent à l'aide de leur téléphone.

La décision de créer l'application Grammarly en tant que clavier natif a présenté à notre équipe des problèmes fascinants et complexes dans l'architecture des applications mobiles, l'expérience utilisateur, les performances et les tests. Cet article de blog portera en particulier sur Android, où l'application Grammarly est unique: contrairement à la plupart des applications Android, il ne s'agit ni d'une activité ni d'un fragment. L'interface utilisateur est contrainte et en constante évolution. Chaque pression de touche sur le clavier grammatical déclenche un certain nombre d'événements simultanés. Le clavier recherche des suggestions à partir de ses modèles sur l'appareil, et au fur et à mesure que cette demande est traitée, il vérifie également avec le backend pour obtenir une analyse approfondie du texte que vous avez tapé. Un autre fil récupère une liste de synonymes pour le mot courant et les affiche dans la «bande de suggestions» au-dessus des touches. Tout cela se produit en 10s de millisecondes. Si cela vous semble intéressant, lisez la suite pour découvrir à quoi ressemble la création d'une application de clavier.

Claviers Android

Un clavier sur Android est appelé un IME ou un éditeur de méthode de saisie. le Android Documentation donne un bon aperçu de la mise en œuvre d'un IME, nous allons donc mentionner brièvement les principaux points. Sur Android, un clavier est une application dont l'état et la visibilité sont contrôlés par un composant de service appelé le InputMethodService. Le clavier implémente également un KeyboardActionListener, dont la tâche principale est de gérer les événements tactiles tels que la saisie et les gestes, puis d'envoyer des mises à jour à l'éditeur de texte via le InputConnection interface.

En ce qui concerne les fonctionnalités, la plupart des claviers ont une bande au-dessus des touches où des suggestions prédictives sont affichées pour aider l'utilisateur à taper plus rapidement. Et pour améliorer la précision, les claviers utilisent des modèles intelligents pour prédire le caractère le plus susceptible d'avoir été touché par l'utilisateur. Enfin, pour implémenter la correction automatique, la plupart des claviers tentent également de prédire le mot que l'utilisateur tente de saisir.

Exigences supplémentaires pour la grammaire

Comme la plupart des claviers Android, Grammarly exécute ses propres modèles de correction automatique et de prédiction du mot suivant; nous avons également écrit notre propre algorithme de reconnaissance des gestes pour donner aux utilisateurs la possibilité de glisser pour taper. Mais notre objectif plus large était d'aller au-delà des fonctionnalités de base décrites ci-dessus pour aider l'utilisateur à mieux écrire. À cette fin, nous voulions fournir des suggestions d'écriture plus approfondies en affichant les mêmes types d'alertes que les utilisateurs reçoivent de l'extension de navigateur de Grammarly. Le cas échéant, nous voulions également que le clavier de Grammarly suggère de bons synonymes pour le mot actuel.

Pour ce faire, notre clavier s'appuie sur deux sources d'informations: (1) les modèles rapides stockés sur l'appareil dans le cadre de l'application pour la correction automatique, la prédiction du mot suivant, les synonymes et la saisie gestuelle; et (2) le backend de Grammarly, qui effectue une analyse plus approfondie au niveau des phrases et des paragraphes. En combinant ces sources, le clavier est capable de présenter à tout moment les suggestions d'écriture les plus pertinentes à l'utilisateur.

Domaines d'intervention pour la création du clavier de Grammarly

Ci-dessous, nous décrivons certaines des complexités intéressantes auxquelles nous avons été confrontés lors de la création d'une application qui doit servir à la fois d'assistant d'écriture intelligent et d'IME rapide et fiable. Nous explorerons quelques-uns de ces sujets plus en détail dans les prochains articles de blog.

Activation des micro-interactions à chaque pression de touche

Pour le clavier grammatical, nous devions trouver un moyen d'intégrer plus d'informations dans un espace déjà petit et uniforme. Il est important que les utilisateurs voient les suggestions de Grammarly, mais en même temps, les informations affichées lors de la frappe peuvent rapidement devenir accablantes ou distrayantes. Nous avons opté pour une UX où les suggestions du backend apparaissent comme cartes d'alerte à côté du logo Grammarly sur la bande de suggestions, empilant s'il y a plusieurs cartes. Pour permettre aux utilisateurs de voir des détails supplémentaires pour chaque alerte, nous avons introduit un vue de révision qui remplace la disposition du clavier et affiche des cartes avec des descriptions sur les modifications susceptibles d'être appliquées au texte.

L'espace étant si limité, nous avons joué avec une autre dimension: le temps. Lorsque l’utilisateur a arrêté de taper pendant plus de quelques secondes, la bande supérieure du clavier change d’affichage des prédictions de saisie semi-automatique ou du mot suivant et affiche à la place des suggestions de synonymes.

Développement d'une machine d'état pour les vues de l'interface utilisateur

L'interface utilisateur d'un clavier est très modeste. Il se compose principalement d'une vue qui montre différentes suggestions au-dessus du clavier et d'une autre vue contenant la disposition du clavier elle-même. Même si un clavier peut sembler statique par rapport à une application typique, l'interface utilisateur est en constante évolution. Un clavier ne peut pas créer de nouveaux écrans et vues. Au lieu de cela, en fonction de l'événement, les vues sont soit désactivées, soit superposées, souvent à plusieurs endroits à la fois. Par exemple, lorsqu'un utilisateur appuie sur la touche des symboles, la disposition du clavier change en remplaçant les vues des touches de l'alphabet par de nouvelles, et lorsqu'un utilisateur appuie sur le logo Grammarly, la disposition du clavier passe à la vue de révision. Les trajectoires pour la saisie gestuelle sont dessinées au-dessus de la disposition du clavier.

La bande de suggestions de Grammarly comporte plus de parties mobiles que la plupart. Sa hiérarchie d'affichage comprend les éléments suivants: le logo Grammarly, des suggestions de correction automatique ou de mot suivant, des cartes d'alerte, des synonymes, un badge au-dessus du logo Grammarly qui affiche le nombre d'alertes dans la pile et des invites utilisateur importantes. Toutes ces vues doivent coexister de manière transparente dans la bande de suggestions, en alternance ou en superposition selon les besoins, avec des animations de transition spécifiques. Nous avons conçu une architecture basée sur l'état pour rendre l'interface utilisateur fluide et agréable. La sortie de l'inspecteur de mise en page ci-dessous indique le nombre de vues qui composent la bande de suggestions.

Gérer plusieurs pipelines et threading

Le flux constant de suggestions et d'alertes vers la bande de suggestions est alimenté par différents pipelines qui obtiennent des prédictions des modèles sur l'appareil et du backend Grammarly. À chaque pression de touche, une séquence complexe d'événements se déroule sur plusieurs threads.

Dans le thread principal, la pression sur la touche est validée dans l'éditeur et apparaît dans la zone de texte. Mais en parallèle, un autre thread obtient des prédictions de mot suivant ou de correction automatique. Si la pression sur une touche était un caractère normal, comme «g», les modèles tentent de prédire quel mot l'utilisateur est en train de saisir, comme «excellent». S'il s'agit d'un espace ou d'un autre séparateur, les modèles tentent de prédire le ou les mots suivants, comme "travail" après "excellent". Un thread différent récupère les synonymes du dernier mot tapé, qui seront affichés si l'utilisateur fait une pause de plus de quelques secondes.

Ces différentes suggestions passent par un pipeline RxJava et se connectent aux modèles de vue de la hiérarchie de vues de la bande de suggestions. Tout cela se produit en environ 20 millisecondes.

Mais cela ne couvre que ce qui se passe sur l'appareil. Pendant ce temps, un thread séparé crée des différences du texte et les envoie au back-end par lots. Le clavier utilise des chaînes de Deltas, un format expressif pour décrire les opérations d'insertion, de conservation ou de suppression, pour communiquer ce qui a changé. Lorsque le backend reçoit ces modifications, ses modèles fournissent des suggestions d'écriture supplémentaires qui peuvent être au niveau de la phrase ou même du paragraphe. Ces alertes sont empilées sous forme de cartes à côté du logo Grammarly dans la bande de suggestions.

Le défi de la gestion de ces différents pipelines est similaire au maintien cohérence du cache. Avec différents threads manipulant les données à la fois, nous devons faire attention à éviter les corruptions. Si le texte dans l'éditeur est modifié au même endroit que le backend suggérait une modification, cette modification doit être invalidée. Si le texte précédant une suggestion est modifié, les limites de cette suggestion doivent être déplacées vers la droite pour tenir compte. Sinon, la suggestion pourrait être appliquée à la mauvaise section et corrompre le texte.

Modifier le texte

La plupart des claviers ne conservent que les derniers mots au fur et à mesure que l'utilisateur tape. Cependant, étant donné que les suggestions d'arrière-plan de Grammarly peuvent s'appliquer à n'importe quelle partie du texte, le clavier doit gérer la modification de n'importe quelle partie du texte de l'utilisateur. Ceci est particulièrement difficile car les modifications du texte peuvent se produire dans n'importe quel ordre et à partir de n'importe quelle source: clés individuelles, suggestions ou alertes back-end.

Pour gérer ces modifications, le clavier doit avoir un accès rapide à l'intégralité du texte de l'utilisateur. Bien qu'il soit possible d'obtenir le texte de l'éditeur avec le InputConnection l’interface, c’est un appel IPC, ce qui coûte cher. Pour éviter d'avoir à effectuer ces appels fréquemment, le clavier maintient un cache du texte dans l'éditeur. Là encore, des règles prudentes doivent être appliquées pour éviter de corrompre le texte. Celles-ci incluent le rechargement du cache lorsque l'utilisateur ne tape pas ou lorsque le clavier récupère des synonymes à partir des modèles sur l'appareil après que l'utilisateur a fait une pause de quelques secondes.

Assurer des performances rapides

Les claviers doivent être très vite. Tout changement de code qui ajoute ne serait-ce que quelques dizaines de millisecondes à n'importe quelle partie du flux d'appels entraînera un décalage visible et dégradera l'expérience de frappe de l'utilisateur. Nous essayons constamment d'optimiser les flux d'appels pour rendre l'expérience utilisateur la plus vive possible.

Nous pensons à la performance sur deux fronts. Le premier est heure de début du clavier. Le clavier est un service permanent, par opposition à une application que l'utilisateur peut supprimer en le faisant glisser hors de la liste des applications récemment utilisées d'Android. Si le processus de clavier est tué, le système d'exploitation le redémarre immédiatement. Un démarrage à froid (lorsque l'application clavier redémarre à partir de zéro) est une opération coûteuse, et pendant que les modèles sur l'appareil sont en cours d'initialisation, l'utilisateur peut voir des espaces réservés vides pour des suggestions. Si le clavier fonctionne mais que des parties importantes de l'application doivent être réinitialisées, cela s'appelle un démarrage à chaud, et cela peut également être coûteux. Toutes les autres sessions de clavier sont des démarrages à chaud, qui devraient être aussi rapides que possible. Étant donné que le système d'exploitation décide du moment des démarrages à froid et à chaud, notre objectif est d'accélérer le temps de démarrage du clavier dans tous ces scénarios.

La deuxième mesure de performance sur laquelle nous nous concentrons est latence des touches. Lorsque l'utilisateur appuie sur une touche, plusieurs pipelines s'exécutent pour générer des suggestions et diverses vues font la transition dans et hors de la bande de suggestions. Le temps nécessaire à toute cette activité pour se terminer sur le thread de l'interface utilisateur est ce que nous considérons généralement comme la latence de la pression de touche. Cela se traduit par la réactivité du clavier à chaque pression de touche.

C'est un domaine sur lequel nous concentrons actuellement beaucoup d'efforts et nous visons à réaliser des gains significatifs dans les mois à venir. La façon dont nous abordons ce problème consiste à réduire l'utilisation du thread d'interface utilisateur et à minimiser les changements de contexte entre les différents threads dans les pipelines de traitement de texte. Lors du profilage, nous avons identifié des opérations chronophages sur le thread d'interface utilisateur qui retardent parfois le traitement de la prochaine pression de touche entrante. Nous avons également découvert que lorsque nous générons de nombreuses suggestions sur l'appareil, la mémoire allouée augmente rapidement, ce qui entraîne un garbage collection fréquent qui peut nuire aux performances de l'interface utilisateur.

Aller au-delà des tests manuels

Tester un clavier est un problème difficile (mais super intéressant!). La vue du clavier est apparemment statique, mais le clavier peut être dans l’un des nombreux états, en fonction du texte de l’utilisateur, de la position du curseur ou du caractère avant ou après le curseur. Le clavier Grammarly a également des suggestions du backend qui ajoutent à la complexité. Chaque nouvelle fonctionnalité d'écriture que nous ajoutons peut augmenter de manière non linéaire le nombre de choses à tester.

Notre stratégie de test comprend une combinaison de tests manuels, unitaires, d'instrumentation et d'interface utilisateur. Les tests d'instrumentation pour un clavier sont délicats car le InputMethodService ne se prête pas à une utilisation via un ServiceTestRule. Nous avons constaté que les tests d'instrumentation ne permettent pas de saisir le classeur local du InputMethodService (ces tests ont généralement besoin de ce handle pour appeler les API du service). Nous nous dirigeons donc vers plus de tests unitaires et d'interface utilisateur. Les frameworks de test d'interface utilisateur populaires sont parfaits pour tester l'application ciblée. cependant, nous avons constaté qu'ils ne prennent pas en charge le test de l'IME lui-même. Nous utilisons donc le système intégré d'Android Automate d'interface utilisateur. Pour que la durée du cycle de test reste courte, nous exécutons plusieurs tests en parallèle en utilisant Fourchette.

D'autres défis vous attendent

Nous avons passé en revue le fonctionnement du Grammarly Keyboard, ainsi que les différents domaines d'intérêt de notre équipe: de la recherche de la façon d'optimiser l'UX pour un petit écran à la gestion des pipelines sur l'appareil et back-end avec multithread, pour déterminer comment tester une application Android qui ne se comporte pas comme une application normale.

Dans les articles suivants, nous approfondirons des sujets spécifiques, tels que les pipelines internes, l'orchestration des vues et l'optimisation des performances. Nous explorons quelques modèles d'architecture que nous pouvons appliquer au clavier pour le rendre plus robuste et plus facile à entretenir. Nous essayons de concevoir notre propre architecture basée sur l'état en suivant de bons principes de conception et en empruntant des idées à des paradigmes tels que Model – view – viewModel (MVVM) et Model – view – intent (MVI).

Si vous souhaitez résoudre des problèmes d'architecture complexes, optimiser les performances ou concevoir une expérience utilisateur exceptionnelle, nous serions ravis de vous parler. L'équipe Android de Grammarly est une équipe relativement petite à l'heure actuelle et nous recherchons des personnes qui peuvent se joindre à nous pour créer un assistant d'écriture génial.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *