Week 02

zdroje, lokalizácia, ikona aplikácie, obrázky, prehrávanie médií, manifest

Úvod

  • http://sli.do#ki-smart - anketa a otázky pre prednášajúceho

  • https://join.slack.com/t/ki-smart/signup - slack pre rýchlu komunikáciu

  • (slide) hackaton
  • (slide) coderfest - už budúci utorok, zaregistrujte sa čo najskôr!

  • (slide) OSS Víkend
    • o Linuxe, ale nie len o ňom
    • ak ste sa stretli s niečím (nie niekým) zaujímavým, príďte o tom porozprávať - je to skúsenosť prísť porozprávať pred nie veľmi veľkú skupinu ľudí
  • (slide) enki - šikovná aplikácia pre vaše každonné nešportové workouty - programátorské

  • (slide) prvý šprint - budúci týždeň

Torch Application - Continues

  • dnes budeme pokračovať vo vývoji aplikácie, ktorú sme začali vyvíjať minulý týždeň - baterku
  • (slide)

Zmena textu tlačidla a trieda View

  • Ak si spomenieme na ukážkové správanie aplikácie, po kliknutí na tlačidlo by malo dôjsť k zmene jeho textu. Potrebujeme teda získať referenciu na objekt tlačidla, aby sme vedeli aktualizovať jeho text.

  • Ak sa pozrieme na deklaráciu metódy toggle(), má jeden parameter typu View. Tento parameter je práve referenciou na objekt, nad ktorým k volaniu metódy došlo, čo je v našom prípade práve tlačidlo.

  • (slide) Trieda View je však veľmi význačná, nakoľko akýkoľvek prvok nachádzajúci sa v rozložení aktivity je potomkom tejto triedy. To znamená, že aj samotné tlačidlo, ktoré je inštanciou triedy Button je potomkom triedy View.

  • Pred samotnou zmenou textu teda najprv potrebujeme parameter metódy view pretypovať a to buď priamo v deklarácii metódy alebo vytvoriť novú lokálnu premennú metódy, ktorá vznikne pretypovaním parametra:

    
        public void toggle(View view){
            Button button = (Button)view;
        
            this.isOn = !this.isOn;
            if(this.isOn == true){
                button.setText("Turn Off");
            }else{
                button.setText("Turn On");
            }
        }
        

Zmena textu elementu <TextView>

  • My však budeme chcieť zmeniť obsah textu priamo v aplikácii. Aby sme získali objekt reprezentujúci element <TextView> zo súboru s vzhľadom aktivity, použijeme metódu findViewById() (slide). Táto metóda má jeden parameter, ktorým je identifikátor daného elementu (v Androidovom API je každý element potomkom triedy View, ale o tom neskôr). Identifikátor sa nachádza v atribúte elementu android:id, pričom v kóde je reprezentovaný konštantou pomocou triedy R. K objektu elementu <TextView> sa teda v kóde dostaneme nasledovne:

    
          TextView label = (TextView) findViewById(R.id.label);
          
  • Obsah elementu je možné zmeniť pomocou metódy objektu s názvom setText(). Metóda toggle() bude po uvedených úpravách vyzerať nasledovne:

    
        public void toggle(View view){
            Button button = (Button)view;
            TextView label = (TextView) findViewById(R.id.label);
        
            this.isOn = !this.isOn;
            if(this.isOn == true){
                label.setText("On");
                button.setText("Turn Off");
            }else{
                label.setText("Off");
                button.setText("Turn On");
            }
        }
        

Resources

  • Je najvyšší čas nahradiť textový popisok obrázkom, ktorý bude reprezentovať stav baterky. Preto sa najprv pozrieme na organizáciu projektu a povieme si niečo o zdrojoch aplikácie (slide).

  • používajú sa na zabezpečenie špecifických konfigurácií, ako napr. rozličné jazykové verzie, rozlíšenia alebo orientácia aplikácie

  • nachádzajú sa v priečinku /res (slide):
    • drawable/ - Bitmap files (.png, .jpg, .gif)
    • layout/ - XML files that define a user interface layout
    • mipmap/ - Drawable files for different launcher icon densities.
    • values/ - XML files that contain simple values, such as strings, integers, and colors.
    • raw/ - Arbitrary files to save in their raw form.
  • Once you provide a resource in your application, you can apply it by referencing its resource ID. All resource IDs are defined in your project’s R class, which is automatically generated.

  • A resource ID is always composed of (slide):
    • The resource type: Each resource is grouped into a “type,” such as string, drawable, and layout.
    • The resource name, which is either: the filename, excluding the extension; or the value in the XML android:name attribute, if the resource is a simple value (such as a string).
  • Trieda Resources umožňuje pristupovať ku zdrojom vašej aplikácie.

i18n and l10n

Obrázky a <ImageView>

  • Ak chceme v aplikácii používať obrázky a chceme, aby sa stali súčasťou aplikácie, musíme ich nakopírovať do priečinku /res/drawable.

  • Pre projekt budeme potrebovať tieto obrázky:

    Obr. 1: bulb_on
    Obr. 1: bulb_on
    Obr. 2: bulb_off
    Obr. 2: bulb_off
  • Následne aktualizujeme kód. Pre nastavenie obrázku objektu typu ImageView použijeme metódu setImageResource(), kde parametrom je identifikátor zdroja obrázku. Ak sme obrázky úspešne pridali do projektu, Android Studio rozpozná všetky obrázkové zdroje. Keď teda budeme vkladať parametre do metódy setImageResource(), bude nám priamo posúvať dva zdroje: R.drawable.bulb_on a R.drawable.bulb_off:

    
        public void toggle(View view){
            Button button = (Button) view;
            ImageView image = (ImageView) findViewById(R.id.image);
        
            this.isOn = !this.isOn;
            if(this.isOn == true){
                image.setImageResource(R.drawable.bulb_on);
                button.setText(R.string.turn_off);
            }else{
                image.setImageResource(R.drawable.bulb_off);
                button.setText(R.string.turn_on);
            }
        }
        

Ošetrenie kliknutia na obrázok

  • Niektorí používatelia majú tučnejšie pršteky ako iní, takže môžu mať v kritických situáciách problém trafiť sa na tlačidlo. Pre nich by bolo výrazne jednoduchšie, ak by stačilo klikn��ť na obrázok a tým zasvietiť, resp. zhasnúť svetlo.

  • Podobne, ako v prípade tlačidla, môžeme upraviť vlastnosti obrázka v rozložení aplikácie. Konkrétne opäť pridáme do atribútu onClick názov metódy, ktorá sa spustí po kliknutí na obrázok. Opäť budeme volať metódu toggle().

  • Tentokrát však pôvodná metóda nebude pracovať správne - ak klikneme na tlačidlo, bude pracovať správne. Ak však klikneme na obrázok, zosype sa. To je spôsobené tým, že očakávame, že parameter view bude tlačidlo. Aby sme tomuto problému predišli, upravíme funkciu toggle() tak, že bude nezávislá od parametra view:

    
        public void toggle(View view){
            Button button = (Button) findViewById(R.id.button);
            ImageView image = (ImageView) findViewById(R.id.image);
        
            this.isOn = !this.isOn;
            if(this.isOn == true){
                image.setImageResource(R.drawable.bulb_on);
                button.setText(R.string.turn_off);
            }else{
                image.setImageResource(R.drawable.bulb_off);
                button.setText(R.string.turn_on);
            }
        }
        

Ikona Aplikácie

  • Ikona aplikácie zatiaľ vyzerá dosť trápne. A pokiaľ by sme chceli aplikáciu naozaj predávať, chcelo by to niečo atraktívnejšie a sexi. Pozrieme sa teda na to, ako tento nedostatok našej aplikácie opraviť.

  • Keď sme hovorili o zdrojoch (resources) aplikácie, hovorili sme aj o tom, že priečinok /res/mipmap/ obsahuje ikony, ktoré reprezentujú práve spúšťač aplikácie.

  • Aby sme sa veľmi nenamakali, môžeme pre tvorbu ikony využiť rozličné nástroje. Jedným z nich je aj on-line nástroj Android Asset Studio, ktorý 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.

  • My si teda jednoduchú ikonu aplikácie vytvoríme. Ako podklad použijeme nasledujúcu ikonu:

    Obr. 3: launcher icon
    Obr. 3: launcher icon
  • Asset Studio spustíme z menu File > New > Image Asset alebo kliknutím pravého tlačidla na položku projektu a následne New > Image Asset. V ňom následne načítame ikonu aplikácie a upravíme podľa vlastných požiadaviek:

    Obr. 4: Asset Studio
    Obr. 4: Asset Studio
  • Nástroj Android Asset Studio následne vygeneruje ikony aplikácie v niekoľkých rozlíšeniach, resp. formátoch:

    • xxxhdpi (extra-extra-extra-high) ~ 640dpi
    • xxhdpi (extra-extra-high) ~ 480dpi
    • xhdpi (extra-high) ~ 320dpi
    • hdpi (high) ~ 240dpi
    • mdpi (medium) ~ 160dpi
    • ldpi (low) ~ 120dpi

    Poznámka

    Podobne je to aj s postfixmi pre priečinok drawable/.

  • Od tohto momentu všade, kde je použitá ikona aplikácie, uvidíme novú ikonu.

  • Miesto, kde definujeme, ktorá ikona sa v skutočnosti použije ako ikona aplikácie, sa nachádza v manifeste aplikácie (v súbore AndroidManifest.xml). Konkrétne sa jedná o atribút elementu <application> v tvare:

    
          android:icon="@mipmap/ic_launcher"
          

Playing Media

  • Aplikácia je už síce funkčná, ale môžeme spestriť jej používanie pridaním zvuku pre stlačenie tlačidla. S tým nám pomôže trieda MediaPlayer.

  • Samotné zvukové súbory je potrebné uložiť do priečinku /res/raw/. V kóde budú následne dostupné ako R.raw.file.

  • Zapnutie a vypnutie baterky bude reprezentované týmto zvukom (možno pre niektorých aj pomerne známym, pokiaľ ste s dr. Freemanom strávili s pajserom v jednej ruke a baterkou v druhej množstvo nezabudnuteľných chvíľ).

  • Ak chcem daný zvuk následne prehrať, použijem tento fragment kódu:

    
        MediaPlayer player = MediaPlayer.create(this, R.raw.sound);
        player.start();
        
  • Kvôli správnemu načasovaniu “šťuknutia” je vhodné spustiť prehráva�� ešte predtým, ako dôjde k zmene obrázkov, textov a zasvieteniu blesku. Ideálnym miestom sa javí metóda toggle(), ktorá po implementovaní prehrávača bude vyzerať takto:

    
        public void toggle(View view) {
            MediaPlayer player = MediaPlayer.create(this, R.raw.flashlight_on);
            player.start();
        
            this.isOn = !this.isOn;
            update();
        }
        

Smart Device

  • Otázka na štátniciach od profesora Návrata pri téme súvisiacej s inteligentnou domácnosťou (smart home): “A čo je na tom vlastne to inteligentné?”

  • Podobne je to aj s nami - doteraz sme robili aplikáciu na zariadení, ktoré má prívlastok smart. V čom je však výnimočné toto chytré alebo inteligenté zariadenie v porovnaní s bežnými počítačmi?

  • Definícia chytrého zariadenia podľa wikipédie: “A smart device is an electronic device, generally connected to other devices or networks via different wireless protocols such as Bluetooth, NFC, WiFi, 3G, etc., that can operate to some extent interactively and autonomously.”

  • Definícia chytrého telefónu podľa wikipédie: “A smartphone or smart phone is a mobile phone with an advanced mobile operating system which combines features of a personal computer operating system with other features useful for mobile or handheld use. They typically combine the features of a cell phone with those of other popular mobile devices, such as personal digital assistant (PDA), media player and GPS navigation unit.”

  • Naša aplikácia zatiaľ nie je nijak výnimočná a nijak sa nelíši od bežných aplikácií spustených na hlúpom zariadení. Rozšírime teda jej funkcionalitu tým, že do nej integrujeme použitie blesku fotoaparátu tak, aby fungoval ako baterka.

Package Manager

  • Ak teda chceme pracovať s bleskom, musíme získať objekt kamery. Nie všetky zariadenia sú však s kamerou vybavené, preto by bolo dobré pred získaním tohto objektu overiť, či sa kamera v systéme vôbec nachádza. Nemusíme sa však nakoniec pýtať priamo na kameru, ale stačí sa opýtať na to, či máme k dispozícii len blesk.

  • (slide) Na to, aby sme overili, či je zariadenie obsahuje potrebnú vlastnosť, použijeme triedu PackageManager, ktorú je možné získať priamo z aktivity volaním metódy getPackageManager(). (slide)

  • Správca balíčkov plní podobnú funkcionalitu ako je správca balíčkov v linuxovom systéme. Pomocou neho je napr. možné získať zoznam nainštalovaných balíčkov alebo aplikácií v systéme.

  • My sa však pomocou neho budeme snažiť zistiť, či naše zariadenie podporuje požadovanú vlastnosť, resp. funkcionalitu. To docielime volaním metódy hasSystemFeature() inštancie triedy PackageManager.

  • (slide) Požadovaná konštanta pre overenie, či je alebo nie je zariadenie vybavené bleskom, sa volá PackageManager.FEATURE_CAMERA_FLASH. Konštanty pre overenie ostatných vlastností môžete nájsť na stránke s dokumentáciou tejto triedy.

  • Následne vieme vytvoriť kód pre overenie existencie požadovanej vlastnosti v zariadení:

    
        PackageManager pm = getPackageManager();
        if(pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH) == false){
            Log.e(TAG, "This device doesn't support Camera Flash.");
            Toast.makeText(this, 
                "This device doesn't support Camera Flash.", 
                Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        
  • Triedu Log aj Toast už poznáme, ale neznámou je v tomto kóde metóda finish().

Metóda finish()

  • (slide) Metóda aktivity. Metóda sa volá vtedy, keď aktivita skončila a má byť zatvorená, resp. ukončená. Nedôjde však k okamžitému ukončeniu aktivity, ako je to v prípade napr. volania metódy System.exit(). Metóda, ktorá túto metódu zavolala, sa najprv korektne ukončí.

Permissions

  • Ak momentálne aktualizujeme metódu onCreate() o uvedený fragment kódu, výsledok bude vyzerať nasledovne:

    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        
            // init camera
            PackageManager pm = getPackageManager();
            if(pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH) == false){
                Log.e(TAG, 
                    "This device doesn't support Camera Flash.");
                Toast.makeText(this, 
                    "This device doesn't support Camera Flash.", 
                    Toast.LENGTH_LONG).show();
                finish();
                return;
            }
        }
        
  • Ak následne spustíme takto upravenú aplikáciu v emulátore, zobrazí sa nám na obrazovke Toast a v LogCate hláška: This device doesn't support Camera Flash. Preto bude ďalšie spúšťanie prebiehať na živom zariadení.

  • Následne sa už len stačí dostať ku objektu kamery prostredníctom volania:

    
        Camera camera = Camera.open();
        
  • Ideálne z nej rovno spraviť členskú premennú:

    
        private Camera camera;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ...
            this.camera = camera;
        }
        

    Upozornenie

    Ak vytvárate aplikáciu pre API Level 21 a vyššie, vývojové prostredie vám začne tento fragment kódu označovať ako Deprecated (zastaralé). Je to tým, že túto triedu nahradilo nové Camera API. Nové API je však veľmi komplexné. Pre potreby tejto ukážkovej aplikácie si vystačíme so starým API, ktoré je stále k dispozícii.

  • Ak ale aplikáciu s týmto kódom spustíme, zosype sa s výnimkou java.lang.RuntimeException:

    
        java.lang.RuntimeException: Unable to start activity ComponentInfo{sk.tuke.smart.torch/sk.tuke.smart.torch.MainActivity}: java.lang.RuntimeException: Fail to connect to camera service
        
  • (slide) Problém, ktorý tu nastal, nesúvisí s tým, že by sme kameru s bleskom nemali, ale že k nim v rámci aplikácie nemáme prístup. Každá aplikácia totiž, ak chce využívať špeciálne vlastnosti, ako napr. prístup na internet, pr��stup k súborovému systému, prístup ku kontaktom alebo aj spomínanú kameru, musí ich mať explicitne povolené.

  • (slide) Všetky vlastnosti, ktoré chceme v rámci aplikácie používať, potrebujeme zadenifovať v manifeste aplikácie (v súbore AndroidManifest.xml) nad elementom <application>. V našom prípade to teda bude vyzerať takto:

  • Po aplikovaní tejto zmeny a spustení aplikácie, sa táto znova zosype s rovnakou výnimkou. Od verzie Android Marshmallow totiž došlo k zmene práce s prístupovými právami. Do tejto verzie sa práva povoľovali len raz pri inštalácii aplikácie. Od tejto verzie je však boli predstavené tzv. Runtime Permissions, vďaka ktorým používateľ potrebné právo povolí až vtedy, keď ho aplikácia bude chcieť použiť. Aktuálne sa teda aplikácia zosype kvôli tomu, že príslu��né právo nemá povolené. To je možné zapnúť ručne priamo v OS Android tak, že vojdete do menu nastavení aplikácií, v nej si vyberiete príslušnú aplikáciu, jej prístupové práva a vyklikáte potrebné práva:

    Obr. 5: Postupnosť obrazoviek vedúca k zmene prístupových práv
    Obr. 5: Postupnosť obrazoviek vedúca k zmene prístupových práv
  • Až po povolení prístupu aplikácie k fotoaparátu sa aplikácia nezosype.

  • Samozrejme - toto nie je spôsob, ktorý je používateľsky prívetivý a rozhodne sa takto povolenie/zakázanie prístupových práv nerieši v reálnych aplikáciách. Na to používateľsky prívetivé riešenie sa pozrieme neskôr. Teraz už skúsme rozsvietiť svetlo.

Let there be Light

  • aktualizujeme teda metódu toggle() nasledovne:

    
        public void toggle(View view){
            this.player.start();
        
            Button button = (Button) findViewById(R.id.button);
            ImageView image = (ImageView) findViewById(R.id.image);
            Camera.Parameters params = this.camera.getParameters();
        
            this.isOn = !this.isOn;
        
            if(this.isOn == true) {
                image.setImageResource(R.drawable.bulb_on);
                button.setText(R.string.turn_off);
        
                params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                this.camera.setParameters(params);
                this.camera.startPreview();
            }else{
                image.setImageResource(R.drawable.bulb_off);
                button.setText(R.string.turn_on);
        
                params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                this.camera.setParameters(params);
                this.camera.stopPreview();
            }
        }
        

Conclusion

  • aplikáciu už máme takmer hotovú - dokáže pracovať s bleskom a svietiť

  • keď ju však otočím, zhasne a znova sa rozsvieti

  • keď ju minimalizujem, tak zhasne

  • keď ju otočím, tak okrem toho, že zhasne, aj zabudne, v akom stave bola

  • Nabudúce sa pokúsime všetky tieto problémy vyriešiť. Pozrieme sa na životný cyklus aktivity, pokúsime sa niečo spraviť s orientáciou na ležato a taktiež skúsime vyriešiť problém toho, že prestane svietiť, keď aplikáciu minimalizujem.

Additional Resources