Upozornenia v aplikáciách

prístup k blesku fotoaparátu, overenie existencie blesku, komunikácia s používateľom pomocou Toast-u a Alert dialógu, ukončenie aplikácie, ikona aplikácie, rozličné rozlíšenia cieľových zariadení a ich značenie, Android Asset Studio

Záznam z prednášky

Intro

  • (slide) Stále pracujeme na aplikácii baterka, ktorá sa už po “grafickej” stránke správa podľa prototypu webovej aplikácie z prezentácie.

  • Stále však nesvieti a tento problém sa pokúsime vyriešiť na dnešnej prednáške.

  • Okrem toho však budeme potrebovať ošetriť prípady, kedy bude aplikácia spustená na zariadení, ktoré nie je vybavené bleskom a teda svietiť nebude (napr. emulátor). A taktiež musíme vyriešiť situáciu, keď aplikácia bude spustená na Android-e, ale nebude mať práva na prístup ku blesku.

  • A popri tom všetkom by bolo dobré používateľa vždy informovať o tom, k čomu došlo. A to buď pomocou toast-ov alebo alert-ov.

Buď svetlo!

  • (slide) Rámec React Native nevie priamo pristupovať ku fotoaparátu a ovládať jeho blesk. Ak ho chceme používať, musíme nainštalovať vhodný modul. Pre naše potreby použijeme modul react-native-torch.

  • Modul nainštalujeme príkazom:

    $ yarn add react-native-torch
  • Po nainštalovaní necháme aplikáciu znova zostaviť. Predtým však vyčistíme aktuálne zostavenie priamo v priečinku android/. Spustíme teda nasledovnú postupnosť príkazov:

    $ cd android
    $ ./gradlew clean
    $ cd ..
    $ yarn run android
  • Najprv v kóde z nainštalovaného modulu importneme objekt Torch:

    import Torch from 'react-native-torch';
  • Blesk fotoaparátu rozsvietime a zhasneme volaním funkcie .switchState(state) nad objektom Torch, kde state je hodnota typu boolean. Aktualizujeme teda funkciu toggleState() vo vytváranom komponente aplikácie:

    const toggleState = function () {
        Torch.switchState(!isOn);
        ding.play();
        setIsOn(!isOn);
    };

Device doesn’t have a Torch

  • Prípad, kedy zariadenie nie je vybavené bleskom, bude zastupovať emulátor. Pozrime sa teda na spustenie aplikácie na ňom.

  • Ak aplikáciu spustíme, tak sa spustí normálne. Ak sa však pokúsime baterku zasvietiť, skončíme s hláškou Possible Unhandled Promise Rejection a so správou Error: Bad argument passed to camera service.

  • Táto chyba priamo súvisí s kamerou, kedy dochádza k požiadavke o jej ovládanie na zariadení, ktoré ale nemá blesk.

  • Fragment kódu, ktorý slúži na ovládanie blesku vo funkcii toggleState() teda obaľme do volania try-catch tak, ako je to uvedené v dokumentácii modulu react-native-torch. To znamená, že z funkcie musíme spraviť asynchrónnu funkciu a počkáme si na výsledok volania Torch.switchState() pomocou kľúčového slova await:

    const toggleState = async function () {
        try {
            await Torch.switchState(!isOn);
            setIsOn(!isOn);
        } catch (e) {
            console.log(e);
        }
    };

Upozornenie používateľa

  • V kóde sme síce situáciu o neexistencii blesku vyriešili bez toho, aby beh aplikácie zlyhal, musíme však o vzniknutej situácii informovať aj používateľa. Na to nám totiž rozhodne nebude stačiť vypisovanie do konzoly cez console.log(). A nebude nám stačiť ani vypisovanie cez console.warn() alebo console.error(), ktoré sa síce na obrazovke zariadenia zobrazia, ale v produkčnej aplikácii sa už zobrazovať nebudú.

  • Na upozornenie používateľa môžeme samozrejme použiť niekoľko spôsobov. My sa pozrieme na používanie toast-ov a alert-ov.

Android Toast

  • (slide) Toast poskytuje používateľovi jednoduchú spätnú väzbu o aktuálnej činnosti v podobe malého vyskakovacieho upozornenia (z anlg. popup). Na obrazovke vyplní len toľko miesta, koľko potrebuje na zobrazenie správy. Nedá sa naň nijako kliknúť ani s ním ďalej interagovať, pričom interakcia s pôvodnou aplikáciou funguje bez zmien. Toast po chvíli sám zmizne.

    Android Toast
  • (slide) React Native pomocou svojho ToastAndroid API poskytuje podporu pre prácu s toast-ami v systéme Android pomocou JS modulu. Tento modul v základe obsahuje metódu .show(message, duration), ktorá má podobne ako v systéme Android tieto parametre:

    • message - správa, ktorá má byť pomocou toast-u zobrazená, a
    • duration - časové trvanie, počas ktorého má byť toast zobrazený, a to buď ToastAndroid.SHORT alebo ToastAndroid.LONG
  • Pre použitie toast-u v našej aplikácii upravíme kód funkcie toggleState() tak, že v prípade vzniku výnimky v dôsledku chýbajúcej podpory pre kameru, zobrazíme toast s príslušnou správou:

    // import ToastAndroid first
    import { ToastAndroid } from "react-native";
    
    const toggleState = async function () {
        try {
            await Torch.switchState(!isOn);
            setIsOn(!isOn);
        } catch (e) {
            ToastAndroid.show(
                "No camera available. Go and buy a device \
                with some and come back later.",
                ToastAndroid.SHORT
            );
        }
    };

Multiplatformný Toast

  • Uvedené riešenie však bude fungovať len na platforme Android. Ak by sme chceli toast použiť naprieč rozličnými platformami, potrebujeme nainštalovať potrebný treťostranový modul, ktorý podporu toast-ov na tieto platformy zabezpečí. Našťastie máme k dispozícii na výber hneď niekoľko riešení, ako napr.:

  • Keďže použitie modulu react-native-simple-toast je v podstate identické, ako je tomu v prípade použitia natívneho ToastAndroid API z rámca React Native, ukážeme si, ako budú vyzerať toast-y, ak použijeme modul react-native-toast-message. V krátkosti si teda ukážeme jeho použitie.

    Modul Toast Message
  • Modul teda do projektu najprv nainštalujeme:

    $ yarn add react-native-toast-message
  • Aby sme toast-y mohli zobrazovať, pridáme najprv komponent Toast do komponentu aplikácie:

    return (
        <View style={styles.container}>
        <Pressable onPress={toggleState}>
            <Image source={image} />
        </Pressable>
        <Button
            onPress={toggleState}
            title={isOn === true ? 'Turn Off' : 'Turn On'}
        />
        <Toast
            ref={function (ref) {
            Toast.setRef(ref);
            }}
        />
        </View>
    );
  • Následne už len aktualizujeme funkciu toggleState(), kde v prípade, že zariadenie nemá baterku, zobrazí sa toast:

    const toggleState = async function () {
        try {
            ding.play();
            await Torch.switchState(!isOn);
            setIsOn(!isOn);
        } catch (e) {
            Toast.show({
              type: 'error',
              text1: 'No camera available',
              text2: 'Go and buy a device with some and come back later.'
            });
        }
    };

Alert

  • Toast je jednoduchý a neobťažujúci spôsob notifikovania používateľa, ktorý si od neho nevyžaduje žiadnu interackiu. Na rodziel od neho si dialógové okná vyžadujú okamžitú pozornosť, ktorá je nutná pred ďalším fungovaním aplikácie.

  • (slide) Na prácu s dialógovými oknami poskytuje rámec React Native modul Alert. Výstražné dialógové okná, tzv. alerty, poznáme zo štandardného JavaScript-u v prehliadači. V rámci React Native ho reprezentuje API, ktoré funguje na platformách Android aj iOS. V používaní pre jednotlivé platformy existuje niekoľko rozdielov, ktoré sa dočítate v dokumentácii modulu.

    Alert Dialog
  • Pomocou modulu Alert je možné vytvárať výstražné dialógové okná, ktoré sa skladajú z:

    • titulku / nadpisu,
    • správy, a
    • voliteľne zo zoznamu tlačidiel s možnosťou vlastného ošetrenia ich stlačenia
  • Ak by sme teda chceli výstražné dialógové okno použiť v našej aplikácii, odstránime použitie komponentu Toast a vo funkcii toggleState() vložíme vyvolanie výstražného okna takto:

    // update the import for Alert
    import { Alert } from "react-native";
    
    const toggleState = async function () {
        try {
            await Torch.switchState(!isOn);
            setIsOn(!isOn);
        } catch (e) {
            Alert.alert(
                'No camera available',
                'Go and buy a device with some and come back later.'
            );
        }
    };
  • Samozrejme modul Alert nie je jediný modul, pomocou ktorého je možné vytvárať výstražné dialógové okná. Ďalšie moduly nájdete opäť napr. vo vyhľadávači www.npmjs.com alebo v niektorom z Awesome zoznamov.

Ukončenie aplikácie

  • Ak sa používateľ bude pokúšať kliknúť či už na tlačidlo alebo obrázok na zariadení, ktoré neobsahuje baterku, resp. blesk (napr. v emulátore), zobrazí sa mu vytvorené upozornenie, ktoré sme vytvorili pomocou komponentu Alert. Vzhľadom na to, že na prítomnosti blesku je postavená celá aplikácia, nie je tento prístup veľmi praktický. Vhodnejšie by bolo napríklad aplikáciu rovno vypnúť, keď príde na to, že blesk nemá.

  • Priama podpora pre vypnutie aplikácie v React Native však nie je. Túto funkcionalitu nám však zabezpečí externý modul s názvom react-native-exit-app. Nainštalujeme ho príkazom:

    $ yarn add react-native-exit-app
  • Pred použitím najprv z modulu react-native-exit-app importneme objekt RNExitAPP:

    import RNExitApp from 'react-native-exit-app';
  • Následne vo funkcii toggleState() aktualizujeme ošetrenie stlačenia tlačidla OK v dialógovom výstražnom okne ukončením aplikácie. To je možné zabezpečiť volaním RNExitApp.exitApp();.

    const toggleState = async function () {
        try {
            ding.play();
            await Torch.switchState(!isOn);
            setIsOn(!isOn);
        } catch (e) {
            Alert.alert(
                "No camera available",
                "Go and buy a device with some and come back later.",
                [
                    {
                        text: "Quit",
                        onPress: function () {
                            RNExitApp.exitApp();
                        },
                    },
                ]
            );
        }
    };

Test Before Run

  • Aktuálne teda funguje všetko ako má. Ak sa však zamyslíme nad fungovaním, tak nie je veľmi praktické, aby používateľ aplikáciu musel spustiť a až po kliknutí na obrázok alebo tlačidlo došlo k vyhodnoteniu, či je alebo nie je blesk k dispozícii. Toto naozaj nie je UX, ktorý by sme chceli mať.

  • Ak sa zamyslíme, tak vieme, že kód sa vykonáva v rámci funkcie zhora nadol, pričom funkcia musí vrátiť view komponentu. Ak teda na začiatku kódu vložíme fragment, ktorým overíme, či máme alebo nemáme blesk k dispozícii, vyriešili by sme tento problém.

Ikona aplikácie

  • Todo:

    • vlozit obrazok ikonky z emulatora
    • obrazok pre densities
  • (slide) Každá aplikácia nainštalovaná v zariadení, má aj svoju ikonu, pod ktorou ju môžeme nájsť v zozname nainštalovaných aplikácií. Aktuálne používaná ikona je predvolená, pretože sme ju zatiaľ nijako nenastavovali. To urobíme v tomto kroku.

    Predvolená ikona aplikácie
  • V priečinku android/ sa okrem konfigurácie a zdrojových kódov nachádzajú aj zdroje (resources) aplikácie. Medzi zdroje patrí aj ikona aplikácie, tzv. spúšťača aplikácie. Tie sa nachádzajú v priečinku android/app/src/main/res/.

  • (slide) V tomto priečinku sa nachádzajú priečinky s rozličnými postfixmi na základe rozlíšení cieľových zariadení. Rozličné zariadenia sú totiž dodávané s rozličnými rozmermi obrazoviek (napr. mobilné zariadenia, tablety, telky). Okrem toho je však aj veľkosť jedného pixelu na rozličných zariadeniach rozdielna. To znamená, že existujú zariadenia, ktoré na jeden (štvorcový) palec dokážu zobraziť 160 pixelov, ale sú iné zariadenia, ktoré na ten istý (štvorcový) palec dokážu zobraziť 480 pixelov.

    Qualifier DPI Description
    ldpi ~120dpi low-density screens
    mdpi ~160dpi medium-density screens, baseline density
    hdpi ~240dpi high-density screens
    xhdpi ~320dpi extra-high-density screens
    xxhdpi ~480dpi extra-extra-high-density screens
    xxxhdpi ~640dpi extra-extra-extra-high-density uses
    nodpi all Resources for all densities. These are density-independent resources. The system does not scale resources tagged with this qualifier, regardless of the current screen’s density.
    tvdpi Resources for screens somewhere between mdpi and hdpi; approximately 213dpi. This is not considered a “primary” density group. It is mostly intended for televisions and most apps shouldn’t need it—providing mdpi and hdpi resources is sufficient for most apps and the system will scale them as appropriate. If you find it necessary to provide tvdpi resources, you should size them at a factor of 1.33*mdpi. For example, a 100px x 100px image for mdpi screens should be 133px x 133px for tvdpi.
  • Aby sme sa veľmi nenamakali a neupravovali ikonu pre každý jeden typ zvlášť, môžeme na to použiť on-line nástroj Android Asset Studio. Ten obsahuje niekoľko nástrojov, pričom jedným z nich je práve nástroj na tvorbu ikon spúšťačov. Zrejme po vzore tohto online nástroja vzniklo Asset Studio, ktoré je súčasťou aj Android Studia.

  • (slide) Prejdeme teda do Launcher Icon Generator-a, kde nahráme vlastnú ikonu:

    Launcher Icon (zdroj)
  • Keď budeme spokojní so všetkými úpravami, klikneme na tlačidlo Download ZIP. Stiahneme k sebe balík s upravenými ikonami pre jednotlivé rozlíšenia. Vo vnútri balíka sa nachádza priečinok res/, ktorý rozbalíme cez res/ priečinok v našom projekte.

Conclusion

  • Na to, aby sme problém so spustením špecifického kódu po spustení kontroléru vyriešili, potrebujeme poznať a porozumieť životnému cyklu kontajnera. A ten si predstavíme nabudúce.