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) zatiaľ sú informácie len o hackathone AT&T
(slide) coderfest - už budúci utorok, zaregistrujte sa čo najskôr!
-
- 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 typuView
. 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 triedyButton
je potomkom triedyView
.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ódufindViewById()
(slide). Táto metóda má jeden parameter, ktorým je identifikátor daného elementu (v Androidovom API je každý element potomkom triedyView
, ale o tom neskôr). Identifikátor sa nachádza v atribúte elementuandroid:id
, pričom v kóde je reprezentovaný konštantou pomocou triedyR
. 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ódatoggle()
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 layoutmipmap/
- 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
, andlayout
. - 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 astring
).
- The resource type: Each resource is grouped into a “type,” such as
Trieda
Resources
umožňuje pristupovať ku zdrojom vašej aplikácie.
i18n and l10n
(slide)
i18n - internationalization
l10n - localization
ak chceme lokalizovať aplikáciu do iného jazyka, ako je angličtina, priečinok so zdrojmi pre iný jazyk musí byť pomenovaný s postfixom reprezentujúcim kód daného jazyka
súbor s prekladom bude teda vyzerať nasledovne:
<?xml version="1.0" encoding="utf-8"?> resources> <string name="app_name">Baterka</string> <string name="turn_on">Zasvietiť</string> <string name="turn_off">Zhasnúť</string> <resources> </
následne treba upraviť všetky texty v aplikácii, kde sa uvedené reťazce nenachádzajú, resp. kde sa nachádzajú v surovom tvare na reťazce zo zdrojov (
R.string.turn_on
v kóde, resp.@string/turn_on
v súbore návrhu)
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:
Následne aktualizujeme kód. Pre nastavenie obrázku objektu typu
ImageView
použijeme metódusetImageResource()
, 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ódysetImageResource()
, bude nám priamo posúvať dva zdroje:R.drawable.bulb_on
aR.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ódutoggle()
.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 funkciutoggle()
tak, že bude nezávislá od parametraview
: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:
Asset Studio spustíme z menu
File > New > Image Asset
alebo kliknutím pravého tlačidla na položku projektu a následneNew > Image Asset
. V ňom následne načítame ikonu aplikácie a upravíme podľa vlastných požiadaviek: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é akoR.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ódygetPackageManager()
. (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 triedyPackageManager
.(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
ajToast
už poznáme, ale neznámou je v tomto kóde metódafinish()
.
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:uses-permission android:name="android.permission.CAMERA" /> <application <...
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:
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
- slides
- Resources
- Activity Lifecycle
- Android Asset Studio
- Log - API for sending log output.
- Toasts - A toast provides simple feedback about an operation in a small popup.
- MediaPlayer -
MediaPlayer
class can be used to control playback of audio/video files and streams. findViewById()
- Finds a view that was identified by the android:id XML attribute that was processed inonCreate(Bundle)
.- PackageManager - dokumentácia ku triede
PackageManager