You are on page 1of 11

SPPuRV, Android Tutorijal, Deo II, Str.

1/11

Android Tutorijal, Deo II


U ovom drugom delu tutorijala o OS Android obraene su preostale tri teme tutorijala:
1.
2.
3.
4.
5.

Osnovne aplikacije: aktivnosti i namere (deo I)


Niti, servisi, prijemnici i alarmi (deo I)
Dogaaji korisnike sprege (deo II)
Sprega sa ureajima (deo II)
Naprednije tehnike (deo II)

3. Dogaaji korisnike sprege


U ovom odeljku je ukratko obraeno sledea tri recepata:
1. Presretanje pritiska fizikog tastera (projekat PhysicalKeyPress)
2. Pravljenje menija (projekat BuildingMenus)
3. Korienje tastera SEARCH (projekat SearchDialogExample)
3.1 Recept: Presretanje pritiska fizikog tastera
Ovaj recept ilustruje postupak tzv. presretanja dogaaja koji se deavaju prilikom pritiska
fizikog tastera, koje se postie redefinisanjem odgovarajuih povratnih metoda.
Na Android ureaju postoji vie fizikih tastera, koji mogu da generiu dogaaje, npr.
taster KYECODE_POWER, KYECODE_BACK, KYECODE_MENU, itd. Prilikom
pritiska fizikog tastera OS alje dogaaje tipa KeyEvent povratnim (eng. callback)
metodama aktivnosti/prikaza, koji je u fokusu. Povratne metode za obradu dogaaja
pritiska fizikog tastera su onKeyUp, onKeyDown i onKeyLongPress. Kada neka povratna
metoda vrati rezultat true, OS smatra da je taj dogaaj evidentiran i ne prosleuje ga dalje
drugim komponentama. Izuzetci su:
- Taster HOME obrauje OS i on se ne prosleuje aplikacijama.
- Za tastere BACK, MENU, HOME i SEARCH ne treba obraivati dogaaj
KeyDown, ve treba obraivati dogaaj KeyUp.
Klasa PhysicalKeyPress
Sadri nekoliko primera obrade dogaaja generisanih pritiskom fizikih tastera:
- Pritisak tastera kamere i levog dugmeta DPAD-a se obrauje u funkciji
onKeyDown tako to se prikae poruka i dogaaj se evidentira.
- Pritisak tastera za pojaavanje zvuka se obrauje u funkciji onKeyDown tako to se
prikae poruka, ali dogaaj se ne evidentira, pa se jaina zvuka poveava.
- Pritisak tastera SEARCH se obrauje u funkciji onKeyDown pozivanjem funkcije
startTracking, koja prati dogaaje tastera sve do dogaaja KeyUp, a on se
obrauje u funkciji onKeyUp tako to se ispie poruka i dogaaj se evidentira.
- Pritisak tastera BACK se na ureajima niova 5 obrauje u funkciji
onBackPressed, a na ureajima nieg nivoa obrada zapoinje u funkciji
onKeyDown.

SPPuRV, Android Tutorijal, Deo II, Str. 2/11

public boolean onKeyDown(int keyCode, KeyEvent event) {


switch (keyCode) {
case KeyEvent.KEYCODE_CAMERA:
Toast.makeText(this, "Pressed Camera Button",
Toast.LENGTH_LONG).show();
return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
Toast.makeText(this, "Pressed DPAD Left Button",
Toast.LENGTH_LONG).show();
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
Toast.makeText(this, "Pressed Volume Up Button",
Toast.LENGTH_LONG).show();
return false;
case KeyEvent.KEYCODE_SEARCH:
//example of tracking through to the KeyUp
if(event.getRepeatCount() == 0)
event.startTracking();
return true;
case KeyEvent.KEYCODE_BACK:
// Make new onBackPressed compatible with earlier SDK's
if (android.os.Build.VERSION.SDK_INT
< android.os.Build.VERSION_CODES.ECLAIR
&& event.getRepeatCount() == 0) {
onBackPressed();
}
}
return super.onKeyDown(keyCode, event);
}

3.2 Recept: Pravljenje menija


Ovaj recept ilustruje pravljenje tri vrste menija koje postoje na OS Android:
- Meni opcija. To je glavni meni koji se dobija pritiskom tastera MENU. Moe da
ima najvie est stavki, a ako je posednja opcija More, moe se prei na proirenje
tog menia.
- Meni konteksta. Otvara se kad se due zadri pritisnuti taster.
- Podmeni. Otvara se izborom jedne od stavki iz menija.
Klasa BuildingMenus
U funkciji onCreateOptionsMenu se pravi meni opcija za dodavanje, brisanje i slanje
beleke (eng. note):
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(GROUP_DEFAULT, MENU_ADD, 0, "Add")
.setIcon(R.drawable.icon); //example of adding icon
menu.add(GROUP_DEFAULT, MENU_SEND, 0, "Send");
menu.add(GROUP_DEL, MENU_DEL, 0, "Delete");
return super.onCreateOptionsMenu(menu);
}

SPPuRV, Android Tutorijal, Deo II, Str. 3/11


U funkciji onPrepareOptionsMenu se menja meni opcija (opcija za brisanje poruka se
ukljuuje samo ako ima beleaka, tada je vrednost promenljive itemNum vea od 0):
public boolean onPrepareOptionsMenu(Menu menu) {
if(itemNum>0) {
menu.setGroupVisible(GROUP_DEL, true);
} else {
menu.setGroupVisible(GROUP_DEL, false);
}
return super.onPrepareOptionsMenu(menu);
}

U funkciji onOptionsItemSelected se obrauje izbor opcije:


public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case MENU_ADD:
create_note();
return true;
case MENU_SEND:
send_note();
return true;
case MENU_DEL:
delete_note();
return true;
}
return super.onOptionsItemSelected(item);
}

U funkciji onCreateContextMenu se pravi meni konteksta, koji se u ovom primeru


aktivira duim pritiskom na tekst u prikazu (u tom cilju je tekst registrovan na
odgovarajui nain u funkciji onCreate, pomou poziva funkcije registerForContextMenu); tu je ilustrovano i pravljenje podmenija za prvu stavku menija konteksta:
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
if(v.getId() == R.id.focus_text) {
SubMenu textMenu = menu.addSubMenu("Change Text");
textMenu.add(0, ID_TEXT1, 0, choices[0]);
textMenu.add(0, ID_TEXT2, 0, choices[1]);
textMenu.add(0, ID_TEXT3, 0, choices[2]);
menu.add(0, ID_DEFAULT, 0, "Original Text");
}
}

U funkciji onContextItemSelected se obrauje izbor stavke menija konteksta:


public boolean onContextItemSelected(MenuItem item) {
switch(item.getItemId()) {
case ID_DEFAULT:
bv.setText(R.string.hello);
return true;

SPPuRV, Android Tutorijal, Deo II, Str. 4/11


case ID_TEXT1:
case ID_TEXT2:
case ID_TEXT3:
bv.setText(choices[item.getItemId()-1]);
return true;
}
return super.onContextItemSelected(item);
}

3.3 Recept: Korienje tastera SEARCH


Ovaj recept ilustruje pokretanje operacije pretraivanja pritiskom na taster SEARCH.
Uobiajeno je da postoji i odgovarajua stavka menia, koja samo poziva funkciju
onSearchRequested.
U ovom primeru aktivnost za pretraivanje se pokree u reimu singleTop, tako da je
mogue pokrenuti proizvoljan broj instanci te aktivnosti. Nakon pokretanja aplikacije,
pokree se njena glavna aktivnost definisana klasom MainActivity, koja formira
korisniku spregu na osnovu datoteke main.xml (samo jednostavna pozdravna poruka).
Klasa SearchDialogExample
Nakon pritiska na taster SEARCH poziva se funkcija onCreate, koja proverava da li je u
pitanju namera ACTION_SEARCH, i ako jeste pokree upit nad resursom
my_search.xml:
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Toast.makeText(this, "The QUERY: " + query,
Toast.LENGTH_LONG).show();

Domai zadatak: presresti dogaaje KeyDown i KeyUp za taster SEARCH, kao u receptu
pod 3.1, tako da dogaaj KeyUp bude evidentiran, pa da se aktivira samo ova aktivnost
pretraivanja, a ne i standardna komponenta za pretraivanje.
4. Sprega sa ureajima
U ovom odeljku je ukratko obraeno sledeih tri recepta:
1. Prilagoavanje kamere (projekat CameraExample)
2. Oitavanje orijentacije ureaja (projekat OrientationMeasurements)
3. Pozivanje telefonskog broja (projekat TelephonyManager)
4.1 Recept: Prilagoavanje kamere
Ovaj recept ilustruje nain prilagoavanja kamere.
Upravljanje kamerom na OS Android je omogueno kroz sledee komponente:
- Klasa Camera pristupa fizikoj arhitekturi kamere.

SPPuRV, Android Tutorijal, Deo II, Str. 5/11


-

Klasa Camera.Parameters specificira parametre kamere kao to su veliina slike,


kvalitet slike, reim rada blica i nain pridruivanja GPS (Global Positioning
System) lokacije.
Preklopljene Preview metoda odreuju oblik izlaznog prikaza kamere i pretvaraju
video podatake u tok za prikazivanje na ekranu.
Klasa SurfaceView na najniem nivou hijerarhije stvara povr za crtanje koja
slui kao mesto za prikaz video podataka.

Prilagoavanje kamere zapoinje deklarisanjem prikaza tipa SurfaceView u datoteci


main.xml, koja definie glavni ekran aplikacije. Zatim se dodaje upravljaka sprega na
vrh ovog prikaza korienjem razmetaja (eng. layout) definisanog u datoteci
cameraoverlay.xml. Izmeu ostalog, ovaj razmetaj definie dugme za uzimanje slika:
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="take picture"
/>

Klasa CameraApplication
U funkciji onCreate prozor aplikacije se podeava da zauzme ceo ekran:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);

Povr za crtanje se zatim ispunjava podacima iz kamere. To se radi tako to se najpre


pribavi objekat za pristup povri za crtanje tipa SurfaceHolder, zatim se ova (this)
aktivnost definie kao sprega za povratnu funkciju objekta mSurfaceHolder i na kraju se
definie tip povri kao SURFACE_TYPE_PUSH_BUFFERS, to znai da se na povr smetaju
podaci direktno iz kamere, pri emu objekat za pristup povri nije vlasnik bafera (radi
efikasnog iscrtavanja):
mSurfaceView = (SurfaceView)findViewById(R.id.surface);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(
SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

Zatim se pravi objekat tipa LayoutInflater pomou koga se raspored definisan u


cameraoverlay.xml postavlja preko osnovnog rasporeda main.xml. Nakon toga se locira
dugme takepicture i dodaje mu se prislukiva, u ijoj metodi onClick se poziva
metoda takePicture objekta mCamera:
takepicture.setOnClickListener(new OnClickListener(){
public void onClick(View view){
{

SPPuRV, Android Tutorijal, Deo II, Str. 6/11


mCamera.takePicture(mShutterCallback,
mPictureCallback,mjpeg);
}
}
});

Parametri funkcije takePicture su sledee tri povratne funkcije:


- mShutterCallback definie efekte nakon slikanja (npr. zvuk da je slika uzeta).
- mPictureCallback je funkcija za obradu sirovih podataka.
- mjpeg je povratna funkcija, iji zadatak je kompresija podataka; ona poziva
lokalnu metodu done radi skladitenja slike u sistem datoteka.
U funkciji done se podaci slike dekodiraju iz tipa ByteArray u tip Bitmap pomou klase
BitmapFactory. Zatim se slika skladiti u sistem datoteka pomou dobavljaa sadraja
media (eng. media content provider), koji vraa odgovarajuu URL referencu, i na kraju
se aktivnost unitava pozivom funkcije finish.
Poto klasa CameraApplication implementira spregu SurfaceHolder.Callback, ona
mora da redefinie sledee tri metode:
- surfaceCreated se poziva prilikom stvaranja povri; u ovom primeru ona stvara
objekat mCamera pozivajui metodu open klase Camera.
- surfaceChanged se poziva nakon stvaranja povri, kad god se povr promeni
(npr. njena veliina ili format); u ovom primeru ona pribavlja i postavlja
parametre kamere a zatim pokree formiranje pregleda slike pozivajui metodu
startPreview objekta mCamera.
- surfaceDestroyed se poziva izmeu uklanjanja povri iz prikaza i unitavanja
povri; u ovom primeru se zaustavlja pregled slike pozivom metode stopPreview
objekta mCamera i zatvara se kamera pozivom metode close klase Camera.
Prikaz slike na OS Android nije standardizovan, pa je na nekim ureajima potrebno
dodati eljenu orijentaciju slike, npr. za pejza (eng. landscape) u metodi onCreate treba
dodati ovaj poziv:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

4.2 Recept: Oitavanje orijentacije ureaja


Ovaj recept ilustruje nain oitavanja orijentacije ureaja (eng. rotational attitude).
Koordinatni sistem ureaja je definisan tako da je x osa u smeru krae stranice ureaja, y
osa u smeru due stranice, a z osa u smeru od ekrana. Koordinatni sistem sveta je
definisan tako da je x osa unakrsni (vektorski) proizvod y i z ose, y osa je tangenta na
povr zemlje usmerena prema severu, a z osa je normala na povr zemlje usmerena prema
nebu. Ako je ureaj poloen na sto tako da je ekran okrenut nagore i prema severu, ova
dva koordinatna sistema se poklapaju. U tom sluaju, mera ubrzanja pokazuje (0,0,G) u
smerovima sve tri ose. Na veini lokacija, magnetno polje zemlje je blago usmereno

SPPuRV, Android Tutorijal, Deo II, Str. 7/11


nadole pod nekim uglom , pa ak i kada ureaj pokazuje tano na sever, i zato se to
polje izraava kao (0,Hcos(),-Hsin()).
Kada se ureaj naginje (tilt) ili zaokree (rotate), metoda getRotationMatrix klase
Sensor vraa matrice rotacije R, dimenzije 3x3, za transformaciju iz koordinatnog sistema
ureaja u koordinatni sistem sveta, kao i matricu inklinacije I (tj. rotacije oko x ose),
dimenzije 3x3, radi transformacije iz stvarnog pravca magnetnog polja u idealan sluaj
(0,H,0).
Drugi nain da se doe do orijentacije je pomou metode getOrientation, koja vraa
matricu rotacije R i vektor poloaja attitude, gde attitude[0] sadri azimut
(azimuth), attitude[1] sadri nagib (pitch), a attitude[2] sadri rotaciju (roll).
Frekvencija oitavanja senzora se moe postaviti na jednu od sledeih vrednosti:
- SENSOR_DELAY_FASTEST je najvea frekvencija (perioda od 8 ms do 30 ms
zavisno od ureaja)
- SENSOR_DELAY_GAME je frekvencija podesna za igre (perioda od priblino 40 ms)
- SENSOR_DELAY_NORMAL je standardna frekvencija, koja je podesna za promene
orijentacije ekrana (perioda od priblino 200 ms)
- SENSOR_DELAY_UI je frekvencja podesna za korisniku spregu (perioda od
priblino 350 ms)
Klasa OrientationMeasurements
U funkciji onCreate se najpre pribavi rukovalac senzorima, a zatim se objekat
mySensorListener registruje kao prislukiva meraa ubrzanja i magnetometra:
myManager = (SensorManager)getSystemService(SENSOR_SERVICE);
myManager.registerListener(mySensorListener,
myManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_GAME);
myManager.registerListener(mySensorListener,
myManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
SensorManager.SENSOR_DELAY_GAME);

U funkciji onSensorChanged objekta mySensorListener najpre se auriraju vrednosti


podataka sa senzora, koje su se promenile (meraa ubrzanja ili magnetometra):
int type = event.sensor.getType();
if(type ==
mags =
}
if(type ==
accels
}

Sensor.TYPE_MAGNETIC_FIELD) {
event.values;
Sensor.TYPE_ACCELEROMETER) {
= event.values;

Zatim se odredi pozicija ureaja na osnovu matrice rotacije, ije vrednosti se pretvore iz
radijana u stepene i prikau na ekranu:

SPPuRV, Android Tutorijal, Deo II, Str. 8/11

SensorManager.getRotationMatrix(RotationMat,
InclinationMat, accels, mags);
SensorManager.getOrientation(RotationMat, attitude);
tv.setText("Azimuth, Pitch, Roll:\n"
+ attitude[0]*RAD2DEG + "\n"
+ attitude[1]*RAD2DEG + "\n"
+ attitude[2]*RAD2DEG);

4.3 Recept: Pozivanje telefonskog broja


Ovaj recept ilustruje programsko pozivanje telefonskog broja.
Klasa TelephonyManager
U funkciji onCreate se najpre pribavlja objekat za pristup telefonskoj usluzi:
telManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);

Zatim se dodaje prislukiva stanja telefona:


telManager.listen(new TelListener(),
PhoneStateListener.LISTEN_CALL_STATE);

Na kraju se zapone odlazni poziv:


startActivity(new Intent(Intent.ACTION_CALL,
Uri.parse("tel:14088923812")));

5. Naprednije tehnike
U ovom odeljku su ukratko obraena sledea dva recepta:
1. Razvijanje komponente na jeziku C (projekat NdkExample)
2. Realizacija poziva udaljene procedure (projekat AIDL)
5.1 Recept: Razvijanje komponente na jeziku C
Ovaj recept ilustruje kako Java aktivnost poziva biblioteku funkciju napisanu na C-u.
Najpre se definie C modul cookbook.c:
#include <string.h>
#include <jni.h>
jint factorial(jint n) {
if(n == 1)
return 1;
return factorial(n-1)*n;
}
jint
Java_com_cookbook_advance_ndk_NDK_factorial(JNIEnv* env,
jobject thiz, jint n)

SPPuRV, Android Tutorijal, Deo II, Str. 9/11


{
return factorial(n);
}

Zatim se u datoteci Android.mk napie skipt za pravljenje biblioteke:


LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE
:= ndkcookbook
LOCAL_SRC_FILES := cookbook.c
include $(BUILD_SHARED_LIBRARY)

Klasa ndk
U funkciji onCreate se poziva C funkcija factorial:
tv.setText(" native calculation on factorial :"+factorial(30));

koja je deklarisana kao native funkcija:


public static native int factorial(int n);

Sama C biblioteka se puni na sledei nain:


static {
System.loadLibrary("ndkcookbook");
}

5.2 Recept: Realizacija poziva udaljene procedure


Ovaj recept ilustruje poziv udaljene procedure (RPC, eng. Remote Procedure Call)
izmeu dve aktivnosti, po modelu klijent-server.
Prvo se deklarie AIDL sprega u datoteci IadditionalService.aidl:
package com.cookbook.advance.rpc;
// Declare the interface.
interface IAdditionService {
int factoria(in int value);
}

Na osnovu ove datoteke, Eclipse automatski generie datoteku IadditionalService.java u


direktorijumu gen. Ova datoteka sadri osnovnu klasu udaljene usluge (eng. stub), koju
treba da realizuje komponenta za davanje udaljene usluge (ili kratko udaljena usluga).
Klasa RPCService
Objekat mBinder se deklarie kao osnova za davanje IAdditionalService:

SPPuRV, Android Tutorijal, Deo II, Str. 10/11

IAdditionService.Stub mBinder;

U funkciji onCreate objekat se definie tako da nudi javnu funkciju factorial, koju je
mogue pozvati putem RPC mehanizma:
mBinder = new IAdditionService.Stub() {
public int factoria(int value1) throws RemoteException {
int result=1;
for(int i=1; i<=value1; i++){
result*=i;
}
return result;
}
};

Da bi klijent mogao da pozove tu funkciju, on najpre mora da se povee sa serverom.


Prilikom povezivanja, na strani servera OS poziva funkciju onBind, koja vraa objekat za
povezivanje mBinder:
public IBinder onBind(Intent intent) {
return mBinder;
}

Dalje se definie druga aktivnost, koja radi u zasebnom procesu. Nakon to se na


uobiajeni nain definie korisnika sprega u datoteci main.xml, u datoteku
AndroidManifest.xml je potrebno dodati jo jedan atribut, kojim se od OS zahteva da
pokrene nov proces, nazvan remoteService, koji izvrava aktivnost servera:
<service android:name=".RPCService" android:process=".remoteService"/>

Klasa rpc
Najpre se deklariu objekat usluge service i objekat veze sa uslugom connection:
IAdditionService service;
myServiceConnection connection;

Zatim se deklarie klasa myServiceConnection za uspostavu (i raskid) veze sa uslugom:


class myServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder
boundService) {
service = IAdditionService.Stub.asInterface((IBinder)
boundService);
Toast.makeText(RPC.this, "Service connected", Toast.LENGTH_SHORT)
.show();
}
public void onServiceDisconnected(ComponentName name) {

SPPuRV, Android Tutorijal, Deo II, Str. 11/11


service = null;
Toast.makeText(RPC.this, "Service disconnected",
Toast.LENGTH_SHORT)
.show();
}
}

Nakon toga se definie funkcija initService za uspostavu veze sa serverom (uslugom),


koja se poziva na poetku funcije onCreate:
private void initService() {
connection = new myServiceConnection();
Intent i = new Intent();
i.setClassName("com.cookbook.advance.rpc",
com.cookbook.advance.rpc.RPCService.class.getName());
if(!bindService(i, connection, Context.BIND_AUTO_CREATE))
{
Toast.makeText(RPC.this, "Bind Service Failed",
Toast.LENGTH_LONG)
.show();
}
}

kao i funkcija releaseService za raskid veze sa serverom, koja se poziva iz funkcije


onDestroy:
private void releaseService() {
unbindService(connection);
connection = null;
}

Sam poziv udaljene procedure se obavlja u funkciji onClick dugmeta buttonCalc:


v1 = Integer.parseInt(value1.getText().toString());
res = service.factoria(v1);