Podstawy aplikacji

Czego nauczyliśmy się podczas tworzenia aplikacji WhatsApp na Wear OS

Głośniki

  • Martin Pelant

    Inżynier oprogramowania, Meta

  • Subhag Bhatia

    Inżynier oprogramowania, Meta

Informacje

Informacje o wystąpieniu

Inżynierowie WhatsAppa opowiedzą o tym, jak ulepszyli aplikację na Wear OS na dużą skalę. Podczas tej sesji omówimy decyzje dotyczące architektury i UX, które doprowadziły do integracji Material 3 i wdrożenia widżetów na Wear.

Poznaj wnioski, wzorce i narzędzia, które zespół wykorzystał do zaktualizowania aplikacji i zapewnienia spójnych wrażeń użytkowników.

Podsumowanie

  • Udostępnianie kodu telefonu z Androidem na Wear OS pozwala uniknąć powielania pracy, ale wymaga wstrzykiwania zależności i zastępowania manifestu. Testy powinny porównywać zestawy wielowiązań, aby funkcje modułowe nie znikały bez powiadomienia.
  • Zarządzanie mostkowaniem powiadomień w czasie działania zapobiega powielaniu alertów, umożliwiając jednocześnie niezależne działanie zegarka. Wyłączenie mostkowania przy logowaniu i ponowne włączenie przy wylogowaniu zapobiega podwójnemu powiadamianiu przez odbicia lustrzane telefonu.
  • Migracja do Material 3 na okrągłych wyświetlaczach wymaga zarządzania zmianami zachowania i stylu wizualnego. Prawidłowe używanie TransformingLazyColumn oznacza stosowanie przekształceń tylko na jednym poziomie i unikanie buforowania pozycji za pomocą remember w Compose.
  • Limity wygaśnięcia aplikacji mogą powodować nagłe spadki liczby aktywnych użytkowników, jeśli zegarki nie zostaną szybko zaktualizowane. Przypomnienia o zaplanowanych aktualizacjach kierują użytkowników do Google Play, zanim wygaśnie 90-dniowa kompilacja.

Gemini Notebook

Tematy

  • wear-os
  • material-3
  • jetpack-compose
  • modularyzacja
  • powiadomienia
  • tiles-and-widgets

Transkrypcja

Pobierz transkrypcję

Wprowadzenie

  • Cześć wszystkim.

  • Nazywam się Martin.

  • A ja Subhag.

  • Jesteśmy inżynierami w firmie Meta i pracujemy nad aplikacją WhatsApp na Wear OS.

  • Niedawno zmodernizowaliśmy naszą aplikację.

  • Dziś podzielimy się wnioskami, które wyciągnęliśmy z migracji do wspólnej bazy kodu i tworzenia na jej podstawie nowych funkcji.

  • WhatsApp na Wear OS działa bez telefonu.

  • Po połączeniu działa ono jako osobne urządzenie WhatsApp, w pełni zaszyfrowane, tak jak telefon, z własną bazą danych i połączeniem z serwerem.

  • Dzięki temu działa nawet przez Wi-Fi lub LTE, gdy telefon jest poza zasięgiem.

  • Aplikację WhatsApp na Wear OS wprowadziliśmy w 2023 r.

  • Był on obsługiwany przez kompaktowego, przenośnego klienta napisanego w języku Rust.

  • Dzięki temu szybko przeszliśmy do produkcji.

  • Była to jednak druga, niezależna implementacja WhatsAppa, co oznacza, że każdy protokół, szyfrowanie i funkcję trzeba było utworzyć dwukrotnie.

  • Aplikacja na zegarek zawsze była więc w tyle.

  • Przenośność utrudniała też obsługę tła.

  • Klient obsługiwał własne wątki, liczniki czasu i gniazda, czyli elementy, którymi Android chce zarządzać za Ciebie.

  • Na Androidzie działanie w tle podlega regułom.

  • W trybie drzemki tracisz połączenie z siecią.

  • Gdy proces zostanie zapisany w pamięci podręcznej, czas jego wykonywania będzie ograniczony lub nie będzie go wcale.

  • Aby to zrobić, potrzebujesz usługi, zadania lub pusha.

  • Nasza aplikacja na zegarek musiała więc łączyć je ręcznie.

  • Ale nasza baza kodu na Androida już to robi.

  • Dlatego zdecydowaliśmy się na ponowne wykorzystanie i migrację do naszej bazy kodu Androida, która została przetestowana na miliardach urządzeń i jest już zgodna z zasadami Androida.

Przejście na wspólną bazę kodu

  • Dzięki temu nie trzeba tworzyć nowych funkcji dwukrotnie.

  • Cel był prosty: zachować logikę biznesową i usunąć interfejs.

  • Pozostaje biblioteka bez interfejsu, która używa przepływów Kotlin jako interfejsu API.

  • Problem polega jednak na tym, że nie da się rozplątać aplikacji o takim rozmiarze za jednym razem.

  • Niektóre moduły łączą interfejs z infrastrukturą.

  • Inne zależą od funkcji, których nie potrzebujesz w aplikacji na Wear OS.

  • Pracowaliśmy więc na 2 frontach: kontynuowaliśmy modularyzację i usuwaliśmy lub nadpisywaliśmy to, czego nie mogliśmy jeszcze usunąć.

  • Zacznijmy od modularyzacji.

  • Aby nie uwzględniać kodu w kompilacji, nie wystarczy prosta instrukcja „jeśli to jest aplikacja na telefon”, ponieważ nie usuwa ona kodu.

  • Dlatego w dużym stopniu polegamy na wstrzykiwaniu zależności, zwłaszcza opcjonalnym i z wieloma powiązaniami.

  • Korzystamy z własnego wstrzykiwania zależności, ale jest ono bardzo podobne do tego, co oferuje Dagger.

  • Przykłady są więc w Daggerze.

  • Dzięki opcjonalnemu wiązaniu (w Daggerze jest to BindsOptionalOf) możesz zadeklarować zależność jako opcjonalną, na przykład wywołując backend.

    @Module
    interface CallingModule {
        @BindsOptionalOf
        fun callingBackend(): CallingBackend
    }
    
    class Calls @Inject constructor(
        val backend: Optional<CallingBackend>,
    ) {
        fun start(): CallResult =
            backend.getOrNull()?.start()
                ?: CallResult.NotAllowed
    }
  • Jeśli kompilacja zegarka zawiera moduł, który zapewnia implementację, wstrzyknięty backend będzie obecny, a wywołanie będzie działać.

  • Jeśli pominiesz moduł, będzie on miał wartość null, a interfejs API może zwrócić błąd NotAllowed.

  • Wiązanie wielokrotne działa tak samo, ale w przypadku zbiorów.

    @Module
    interface CoreModule {
        @Multibinds
        fun handlers(): Set<MessageHandler>
    }
    
    @Module
    interface ReactionsModule {
        @Binds @IntoSet
        fun reactions(impl: ReactionHandler):
     MessageHandler
    }
    
    class MessageDispatcher @Inject constructor(
          private val handlers: Set<MessageHandler>,
    )
  • Możesz zadeklarować zbiór za pomocą multibinds, na przykład MessageHandler, a następnie każdy moduł funkcji może dodać własny moduł obsługi za pomocą multibindingu into set.

  • Jeśli nie chcesz obsługiwać płatności, możesz na przykład pominąć cały moduł płatności w kompilacji, a wstrzyknięty zestaw nie będzie zawierał tego modułu obsługi.

  • W ten sposób pytanie o to, czy funkcja jest obsługiwana, jest pytaniem o graf kompilacji.

  • Jednak to rozdzielenie działa w obie strony.

  • W przypadku odwołań bezpośrednich usunięcie modułu spowodowałoby błąd kompilacji.

  • W przypadku opcjonalnego lub wielokrotnego wiązania nie ma niczego, co odwołuje się do modułu, więc kompilacja się udaje, a funkcja po cichu znika lub zostaje zastosowana domyślna opcja.

  • I właśnie to osiągnęliśmy.

  • Refaktoryzacja przeniosła funkcję do osobnego modułu, a nasza aplikacja do kompilacji i aplikacja na zegarek jej nie uwzględniły.

  • W efekcie wiadomości głosowe miały czas trwania równy 0 sekund.

  • Jak temu zapobiec?

  • Musisz przeprowadzić test, aby ponownie uzyskać plakietkę.

  • W naszym przypadku porównujemy zestawy wielowiązań między aplikacją na telefon i aplikacją na zegarek pod kątem interfejsów, które mają znaczenie.

  • Dzięki temu mamy pewność, że zawsze mamy powiązaną właściwą implementację.

  • Ścieżka 2 obejmuje to, czego nie udało nam się jeszcze usunąć.

  • Ponowne używanie modułów pozwoli Ci zaoszczędzić czas, ale wszystko, co deklarują, zostanie scalone z manifestem.

  • Aktywność programu uruchamiającego na telefonie dodałaby dodatkową ikonę aplikacji lub niepotrzebne mechanizmy dostarczania treści uruchamiałyby się wraz z aplikacją.

  • Narzędzie do scalania plików manifestu udostępnia przydatny atrybut tools:node="remove", który umożliwia wyodrębnienie komponentów.

    <!-- remove unused Main activity node -->
    <activity android:name="com.whatsapp.Main"
              tools:node="remove" />
    
    <!-- redirect any calls to a new Activity -->
    <activity-alias
         android:name="com.whatsapp.Main"
         android:targetActivity=".MyNewActivity" />
    
    <provider
    android:name="…MigrationContentProvider"
            tools:node="remove" />
  • Gdy zostaną usunięte, narzędzie do zmniejszania rozmiaru może usunąć również kod.

  • Jeśli coś nadal musi dotrzeć do aktywności, możesz użyć activity-alias, aby przekierować do nowej klasy.

  • Pamiętaj jednak, że te atrybuty dotyczą tylko plików manifestu o niższym priorytecie, więc kolejność scalania decyduje o tym, czy coś zostanie usunięte.

  • Zasoby działają w ten sam sposób.

  • Dzięki ponownemu wykorzystaniu otrzymujesz ciągi tekstowe na telefon bezpłatnie.

  • Niektóre z nich obiecują jednak rzeczy, których zegarek nie jest w stanie zrobić.

  • Przyjrzyjmy się przykładowi na końcu powiadomienia o szyfrowaniu, które znajduje się na początku każdego czatu.

  • Na telefonie kończy się linkiem „Więcej informacji”.

  • Jednak po kliknięciu linku na zegarku nic się nie dzieje.

  • W takim przypadku ponownie deklarujemy ten sam klucz w module o wyższym priorytecie na zegarku i zastępujemy ciąg tekstem „użyj telefonu, aby dowiedzieć się więcej”.

    <!-- phone -->
    <string name="…_disclosure">… Learn more</string>
    
    <!-- wearable override, same key -->
    <string name="…_disclosure">…Use your phone to learn more.</string>
  • Następnie scalanie zasobów wybiera nasze.

  • Potrzebujesz też czegoś, co zapobiegnie przypadkowemu zastąpieniu ciągu znaków.

  • Dlatego utworzyliśmy regułę kompilacji, która sprawdza, czy zastąpienie jest na liście dozwolonych, i jeśli nie jest, powoduje błąd.

Zarządzanie mostkowaniem powiadomień

  • Porozmawiajmy teraz o powiadomieniach.

  • Domyślnie Wear OS przekazuje lub powiela powiadomienia z telefonu.

  • To świetnie, ale działa tylko wtedy, gdy telefon jest połączony.

  • Chcemy, aby WhatsApp działał niezależnie, więc nasza aplikacja publikuje własne powiadomienia.

  • Jeśli jednak mostkowanie pozostanie włączone, każda wiadomość będzie powodować podwójne powiadomienie.

  • Wyłączenie mostkowania rozwiązuje problem z duplikatami.

  • Istnieje statyczna flaga manifestu, ale obowiązuje ona od momentu instalacji, zanim użytkownik się zaloguje.

  • Jeśli więc zainstalujesz aplikację i nie zalogujesz się, nagle stracisz wszystkie powiadomienia, co nie jest zbyt dobre, prawda?

  • Dlatego ustawiamy go w czasie działania za pomocą BridgingManager.

    isRegistered.collect {
      when (it) {
             // watch posts its own
        true  -> bridging.setEnabled(false)
     // phone bridges again
        false -> bridging.setEnabled(true)
      }
    }
    
    fun setEnabled(bridgingEnabled){
       bridgingManager.setConfig(
    BridgingConfig.Builder(context,
    bridgingEnabled)
          .build())
    }
  • Gdy się zalogujesz, wyłączymy mostkowanie.

  • Gdy się wylogujesz, włączymy ją z powrotem.

  • Pamiętaj jednak, że nie ma interfejsu API do odczytywania wartości, a zegarek może je utracić.

  • Dlatego zapisujemy je ponownie, gdy zegarek zostanie odłączony od ładowarki lub gdy aplikacja zostanie zaktualizowana.

  • Ostatni temat, który chcę omówić, to wylogowywanie się.

  • Gdy odłączysz zegarek od konta, użytkownicy będą oczekiwać, że wiadomości zostaną usunięte.

  • Usuwamy więc wszystko – wiadomości, klucze, ustawienia.

  • Ale jak to zrobić bezpiecznie?

  • Jeśli usuniesz pliki, gdy wątki w tle nadal będą w nich zapisywać, wystąpią wyjątki SQLite lub, co gorsza, zapisy w nowo utworzonych bazach danych.

  • Nie można wyczyścić stanu procesu bez jego zatrzymania.

  • Android udostępnia interfejs API clearApplicationUserData, który usuwa dane i zatrzymuje aplikację, ale też resetuje uprawnienia.

  • W przypadku niektórych producentów OEM mostkowanie powiadomień jest powiązane ze zgodą na wyświetlanie powiadomień, więc jeśli używasz tej funkcji, możesz nagle utracić też możliwość wyświetlania powiadomień z telefonu.

  • Dlatego sami zatrzymujemy ten proces.

    clearActivityBackStack()
    
    // enumerate our own, kill each
    killOtherProcesses()
    
    // no threads alive — now safe
    deleteDataDirectories()
    
    // show logged out confirmation
    firePostCleanupIntent()
  • Tworzymy niewielką usługę w procesie pomocniczym, który nie ma DI ani menedżera zadań, więc nie uruchamia reszty aplikacji.

  • Po wylogowaniu się ta usługa kończy wszystkie inne procesy w naszej aplikacji i dopiero wtedy usuwa wszystkie dane, gdy nic nie zakłóca jej działania.

  • Ponieważ nie używamy clearApplicationUserData, uprawnienia pozostają i możemy włączyć mostkowanie powiadomień, aby powiadomienia z telefonu wróciły.

  • To wszystko, jeśli chodzi o część 1. Teraz przekażę głos Subhagowi, który opowie o odświeżonym interfejsie.

Modernizacja interfejsu Wear OS

  • Dzięki, Martin.

  • Po utworzeniu wspólnej infrastruktury skupiliśmy się na tym, aby aplikacja WhatsApp była jak najlepiej dostosowana do zegarka.

  • Pierwszym etapem tej podróży było przejście na Material 3, ale najpierw zapytajmy, dlaczego.

  • Na okrągłym wyświetlaczu, gdzie liczy się każdy piksel, Material 3 zapewnił nam odpowiednie komponenty i podstawy projektowania, dzięki którym mogliśmy zmodernizować WhatsAppa na zegarki.

  • Jeśli spojrzysz na elementy wizualne, na pierwszy rzut oka może się wydawać, że to tylko aktualizacja interfejsu.

  • Przechodziliśmy z Material 2 na Material 3, a to nie mogło się wydarzyć z dnia na dzień.

  • Stary i nowy kod musiały działać obok siebie, a przełączanie między nimi odbywało się za pomocą przełącznika.

  • W rezultacie ekrany, które wizualnie pasowały do Figmy, nadal wykazywały pewne nieprawidłowe zachowania.

  • Na przykład przewijanie za pomocą pokrętła, przesunięcie w prawo, aby wrócić, czy niektóre z najbardziej podstawowych gestów nie działały.

  • Wszystko to oznacza, że migracja systemu projektowania to nie tylko migracja wizualna, ale też migracja zachowań.

  • Jeśli chodzi o zachowanie, rzędy przy krawędziach są węższe i zakrzywione.

  • That's TransformingLazyColumn.

  • Wiersze są przekształcane, aby okrągła ramka ich nie odcinała.

  • Dynamiczny ekran może utrudniać sprawę.

  • Pojawiają się nowe wiadomości, wiersze się przesuwają, a dymki mają różne kształty.

  • Zobaczmy, co to oznaczało w praktyce.

  • Jeśli spojrzysz na lewą stronę, zobaczysz, że bąbelki kurczą się zbyt agresywnie.

  • Każda wiadomość jest wewnętrznie dymkiem zawartym w karcie.

  • Kazaliśmy karcie nadrzędnej zastosować przekształcenie, ale to samo przekształcenie zostało przekazane do dymka podrzędnego.

  • W efekcie dymek podrzędny otrzymuje dwukrotnie większą wartość przekształcenia.

  • Rozwiązaniem było zastosowanie przekształcenia na jednym poziomie, czyli na karcie nadrzędnej.

  • To również kluczowa zasada.

  • We ensured trans- one transform for one item.

  • Kolejny problem: bąbelki, które są małe i wyblakłe na środku ekranu, gdzie powinny być pełnowymiarowe.

  • Rozmiar jest funkcją ich pozycji na ekranie.

  • W naszej początkowej wersji zapisaliśmy dopasowanie rozmiaru za pomocą funkcji zapamiętywania w Compose, ale spowodowało to też zapisanie starszej pozycji.

  • W rezultacie, gdy pojawia się nowa wiadomość i przesuwa starsze dymki w górę, ich przekształcenie nadal opiera się na ich starszej pozycji.

  • To powoduje błąd.

  • Aby rozwiązać ten problem, przestaliśmy zapisywać rozmiar za pomocą wywołania zapamiętywania i usunęliśmy to wywołanie.

  • To też jest nauka.

  • Gdy wiersz jest przenoszony, jego przekształcenie musi zostać przeniesione razem z nim.

  • Tutaj możesz zobaczyć obie poprawki w kodzie.

    // FIX #1: ONE OWNER
    CompositionLocalProvider(
      LocalSurfaceTransformation provides null
    ) {
      M3Card(transformation = t) {
        SharedChatEntry(...)
      }
    }
    
    // FIX #2: CURRENT ITEM SCOPE
    val t = SurfaceTransformation(spec)
    // BUG #1: DOUBLE MORPH
    M3Card(transformation = t) {
      CompositionLocalProvider(
        LocalSurfaceTransformation provides t
      ) { SharedChatEntry(...) }
    }
    
    // BUG #2: STALE ITEM SCOPE
    val t = remember(spec) {
      SurfaceTransformation(spec)
    }
  • W lewym górnym rogu widać, że przekształcenie jest stosowane na 2 poziomach, a w prawym górnym rogu – że jest stosowane tylko na 1 poziomie, czyli na karcie M3.

  • W lewym dolnym rogu używamy wywołania remember, aby zapisać przekształcenie w pamięci podręcznej, ale zapisuje ono również pozycję.

  • W prawym dolnym rogu obliczamy przekształcenie, za każdym razem biorąc pod uwagę najnowszą pozycję bąbelka.

  • Jak widać, efekt jest spójny na wszystkich ekranach, kluczowe treści pozostają blisko środka, a wiersze naturalnie przesuwają się w pobliżu krawędzi.

Obsługa alertów z powiadomieniami na nadgarstku

  • Teraz omówimy powiadomienia.

  • Teraz wyobraź sobie, że czujesz wibracje na nadgarstku, sprawdzasz zegarek i widzisz wiadomość.

  • Jednak chwilę później zegarek na nadgarstku wibruje ponownie i ponownie.

  • Otrzymujesz wiele powiadomień, a gdy je sprawdzasz, za każdym razem widzisz to samo.

  • To bardzo irytujące.

  • Aby rozwiązać ten problem, zadaliśmy sobie 2 pytania.

  • Jakie zdarzenie powinno wywołać powiadomienie użytkownika?

  • Które zdarzenie powinno aktualizować powiadomienia bez powiadamiania użytkownika?

  • Ten błąd występował, ponieważ za kulisami aktualizacje zdjęcia profilowego, pobieranie multimediów i potwierdzenia, które powinny aktualizować powiadomienie w tle, zamiast tego wysyłały alert do użytkownika.

  • Teraz, gdy nadejdzie nowa wiadomość, zegarek zawibruje dokładnie raz, a zdarzenia, które nie wymagają alertu, odświeżą powiadomienie bez dźwięku.

  • To też jest lekcja.

  • Jeśli powiadomienia są jednym z głównych elementów ścieżki użytkownika, musimy zadbać o to, aby każdy alert naprawdę zasługiwał na jego uwagę.

  • Jeśli chodzi o powiadomienia,

  • Wcześniej powiadomienia z WhatsAppa na Wear OS umożliwiały wykonywanie 2 czynności.

  • Możesz przeczytać wiadomość lub otworzyć aplikację.

  • Jeśli chcesz odpowiedzieć bezpośrednio na powiadomienie, wcześniej nie było to możliwe.

  • Wypełniliśmy tę lukę, używając działania powiadomienia na Androidzie z interfejsem RemoteInput API.

  • Użytkownik może teraz wpisać lub podyktować odpowiedź albo kliknąć szybką odpowiedź.

  • Pamiętaj jednak, że wysłanie wiadomości to nie koniec.

  • Jeśli wiadomość zostanie wysłana, oznaczymy czat jako przeczytany i usuniemy powiadomienie.

  • Jeśli wiadomość nie zostanie dostarczona, możesz spróbować ponownie jednym kliknięciem.

  • To również kluczowa kwestia, którą odkryliśmy podczas tworzenia tej funkcji: działanie powiadomienia musi być traktowane jako kompletny, kompleksowy proces.

Migracja z kafelków na widżety

  • Następnie karty i widżety.

  • Karta, jak widać, zajmuje cały ekran, który można otworzyć, przesuwając palcem po tarczy zegarka.

  • Widżety to mniejsze karty, których kilka może zmieścić się na jednym ekranie z możliwością przewijania.

  • Zarówno kafelki, jak i widżety WhatsApp korzystają z tych samych danych.

  • To też jest zwrot akcji.

  • Możesz pomyśleć, że ten interfejs jest rysowany przez naszą aplikację, ale tak nie jest.

  • Opisujemy tylko interfejs i dane, a system je rysuje.

  • Kliknięcie dowolnej ikony nie może uruchamiać kodu aplikacji.

  • Możesz tylko poprosić o otwarcie ekranu.

  • To już koniec lekcji.

  • Jeśli system renderuje interfejs, jest on częścią produktu i wskaźników testowania.

  • Frank i Yuri będą kontynuować rozmowę o widżetach.

Zapobieganie spadkowi liczby aktywnych użytkowników dzięki przypomnieniom o aktualizacjach

  • To prowadzi nas do ostatniego slajdu. Cofnijmy się o krok i zadajmy sobie pytanie: czy ostatecznie nie chodzi nam o to, żeby coraz więcej użytkowników korzystało z naszych aplikacji?

  • Teraz wyobraź sobie, że jako deweloper logujesz się pewnego dnia do panelu i widzisz znaczny spadek liczby aktywnych użytkowników.

  • Przede wszystkim byłoby to bardzo niepokojące.

  • Następnie możesz zacząć debugować.

  • Możesz mieć wrażenie, że doszło do awarii lub że coś jest nie tak z nową wersją.

  • Jednak w naszym przypadku nie było to prawdą.

  • Okazuje się, że każda wersja aplikacji WhatsApp ma wbudowany 90-dniowy okres ważności.

  • Wyobraź sobie, że użytkownicy utknęli na nieaktualnej kompilacji, a nowe kompilacje nie docierają na ich zegarki.

  • W rezultacie użytkownicy nie opuszczają aplikacji, ale przestaje ona działać.

  • Dlatego aktualizowanie aplikacji stało się częścią produktu.

  • Jak widać na ekranie, 15 dni przed wygaśnięciem aplikacji na ekranie pojawi się ten komunikat.

  • Następnie pojawia się ponownie, gdy do końca okresu pozostaje 7, 5, 3, 2 i 1 dzień.

  • Użytkownik może odrzucić powiadomienie lub przejść do Sklepu Play, aby zaktualizować aplikację.

  • Dzięki temu użytkownicy otrzymują kilka przypomnień o zbliżającym się wygaśnięciu aplikacji i mają kilka okazji do jej zaktualizowania.

  • To już ostatni wniosek.

  • Nasza praca jako deweloperów nie kończy się, gdy udostępnimy aktualizację wszystkim użytkownikom.

  • Proces jest zakończony dopiero wtedy, gdy użytkownicy otrzymają nasze aktualizacje.

  • To już koniec.

  • Jeśli chcesz kontynuować rozmowę, zapraszamy na konsultacje.

  • Teraz Frank i Yuri opowiedzą o widżetach na Wear.

  • Dziękujemy.

Kod

Fragmenty kodu

Fragment kodu w języku Kotlin w 02:27 w filmie „Przechodzenie na udostępnioną bazę kodu”
@Module
interface CallingModule {
    @BindsOptionalOf
    fun callingBackend(): CallingBackend
}

class Calls @Inject constructor(
    val backend: Optional<CallingBackend>,
) {
    fun start(): CallResult =
        backend.getOrNull()?.start()
            ?: CallResult.NotAllowed
}
Fragment kodu w języku Kotlin w 02:47 w filmie „Przechodzenie na udostępnioną bazę kodu”
@Module
interface CoreModule {
    @Multibinds
    fun handlers(): Set<MessageHandler>
}

@Module
interface ReactionsModule {
    @Binds @IntoSet
    fun reactions(impl: ReactionHandler):
 MessageHandler
}

class MessageDispatcher @Inject constructor(
      private val handlers: Set<MessageHandler>,
)
Fragment kodu XML w 04:16 w filmie „Moving to a shared codebase”
<!-- remove unused Main activity node -->
<activity android:name="com.whatsapp.Main"
          tools:node="remove" />

<!-- redirect any calls to a new Activity -->
<activity-alias
     android:name="com.whatsapp.Main"
     android:targetActivity=".MyNewActivity" />

<provider
android:name="…MigrationContentProvider"
        tools:node="remove" />
Fragment kodu XML w 05:03 w filmie „Moving to a shared codebase”
<!-- phone -->
<string name="…_disclosure">… Learn more</string>

<!-- wearable override, same key -->
<string name="…_disclosure">…Use your phone to learn more.</string>
Fragment kodu w języku Kotlin w 05:59 w filmie „Zarządzanie mostkowaniem powiadomień”
isRegistered.collect {
  when (it) {
         // watch posts its own
    true  -> bridging.setEnabled(false)
 // phone bridges again
    false -> bridging.setEnabled(true)
  }
}

fun setEnabled(bridgingEnabled){
   bridgingManager.setConfig(
BridgingConfig.Builder(context,
bridgingEnabled)
      .build())
}
Fragment kodu w języku Kotlin w 07:06 w filmie „Zarządzanie mostkowaniem powiadomień”
clearActivityBackStack()

// enumerate our own, kill each
killOtherProcesses()

// no threads alive — now safe
deleteDataDirectories()

// show logged out confirmation
firePostCleanupIntent()
Fragment kodu w języku Kotlin w 10:30 w filmie „Modernizing the Wear OS UI”
// FIX #1: ONE OWNER
CompositionLocalProvider(
  LocalSurfaceTransformation provides null
) {
  M3Card(transformation = t) {
    SharedChatEntry(...)
  }
}

// FIX #2: CURRENT ITEM SCOPE
val t = SurfaceTransformation(spec)
Fragment kodu w języku Kotlin w 10:30 w filmie „Modernizing the Wear OS UI”
// BUG #1: DOUBLE MORPH
M3Card(transformation = t) {
  CompositionLocalProvider(
    LocalSurfaceTransformation provides t
  ) { SharedChatEntry(...) }
}

// BUG #2: STALE ITEM SCOPE
val t = remember(spec) {
  SurfaceTransformation(spec)
}