Programowanie urządzeń mobilnych LAB1 Paweł Fiderek Cel laboratorium Celem laboratorium jest zapoznanie się z możliwościami programowania urządzeń mobilnych w technologii Java MicroEdition. Część teoretyczna - wprowadzenie Urządzenia przenośne o małej mocy obliczeniowej możemy podzielić na klasy w zależności od dostępnej u nich mocy obliczeniowej, pamięci, rodzaju wyświetlacza (rozdzielczość, liczba dostępnych kolorów), sposobu wprowadzania danych (klawiatura telefonu, pełna klawiatura, rysik). J2ME zbudowana jest na zasadzie zbioru specyfikacji konfiguracji, z których każda zawiera minimalny zbiór pakietów i klas przeznaczonych dla konkretnej klasy urządzeń przenośnych. W specyfikacji J2ME zdefiniowane są dwie konfiguracje CDC oraz CLDC. Konfiguracja CDC (ang. Connected Device Configuration) jest konfiguracją środowiska Java ME zdefiniowanych przez Java Community Process. Konfiguracja CDC przeznaczona jest dla urządzeń wyposażonych w ok. 2048 kB pamięci i pracujących z 32-bitowymi procesorami, czyli np. dla wideotelefonów konsol gier, zestawów audio-wideo, PDA. Specyfikacja znajduje się w dokumentacji JSR-36 i JSR-218. Jest ona przeznaczona dla urządzeń posiadających: stałe połączenie z siecią, 32 bitowy procesor, minimum 512KB dostępnej pamięci ROM, minimum 256KB pamięci operacyjnej RAM, graficzny interfejs użytkownika. Konfiguracja CLDC (ang. Connected Limited Device Configuration) jest bardziej ograniczoną wersją CDC i jest zdefiniowany przez Java Community Process. Konfiguracja CLDC jest przeznaczona dla urządzeń wyposażonych w około 512 kB pamięci i pracujących z 16- lub 32-bitowymi procesorami, czyli np. dla telefonów komórkowych, notesów elektronicznych czy pagerów. W stosunku do CDC , CLDC brakuje między innymi: obsługę działań na liczbach zmiennoprzecinkowych, uruchamianie kodu natywnego dzięki JNI (ang. Java Native Interface), obsługę słabych referencji, serializację obiektów, definiowanie przez użytkownika loaderów klas, obsługę mechanizmu debugowania. J2ME - Mobile Information Device Profile (MIDP) MIDP jest uzupełnienie konfiguracji CLDC o odpowiednie klasy języka Java przeznaczone dla urządzeń mobilnych jak np. telefony komórkowe. Specyfikacja MIDP jest rozwijana przez Java Community Process (JCP). W 2000 r. udostępniono wersję 1.0. W 2002 roku wersję 2.0 MIDP. Programy napisane z wykorzystaniem MIDP noszą nazwę MIDletów i uruchamiane są w środowisku K virtual machine. Profil MIDP zawiera bardziej rozbudowaną obsługę platformy Java niż sama konfiguracja CLDC. MIDP wymagają więcej pamięci. Specyfikacja CLDC wymaga przynajmniej 120 kB pamięci RAM (Random Accesss Memory) na przechowywanie specyfikacji MIDP, oraz co najmniej 32 kB na stos. Oprócz tego wymaga minimum 8 kB pamięci nieulotnej, jest ona potrzebna, aby MIDlety mogły zapisywać dane. Urządzenia korzystające z MIDP powinny być wyposażone w ekrany umożliwiające wyświetlenie co najmniej dwóch kolorów w rozdzielczości minimum 96 na 54 pikseli. Oprócz tego, do komunikacji z użytkownikiem posiadać powinny jedno lub więcej następujących mechanizmów: klawiatura telefonu, klawiatura QWERTY, ekran dotykowy. Ich wyposażenie musi także zapewniać obsługę protokołu HTTP 1.1. Instalacja środowiska. Java Platform Micro Edition Software Development Kit 3.0 wraz z podstawową dokumentacją oraz środowisko programistyczne np. o NetBeans w wersji „Java” o Eclipse wraz z odpowiednią wtyczką EclipseME i utworzeniu w danym środowisku nowego projektu odpowiedzialnego za budowanie aplikacji na urządzenia mobilne. Pakiety i klasy wchodzące w skład J2ME - MIDP: java.util.Timer - ułatwienie dla wątków w szeregowaniu zadań, java.util.TimerTask - zadanie które może być szeregowane przez Timer , javax.microedition.rms - mechanizm obsługi zasobu pamięci trwałej, javax.microedition.midlet - definicja aplikacji MIDP, interkacji między nimi oraz środowiska w którym aplikacja działa, javax.microedition.io - obsługa sieci oparta na CLDC, javax.microedition.lcdui - interfejs użytkownika dla aplikacji. Budowa aplikacji MIDP Główna klasa aplikacji (Midlet) to klasa dziedzicząca po klasie java.microedition.midlet.MIDlet. Klasa ta musi posiadać definicje przynajmniej trzech metod: startApp() odpowiedzialnej za inicjalizację MIDlet’u lub powrót jego ze stanu zatrzymania o o o o pauseApp() odpowiedzialnej za wstrzymanie działania aplikacji (np. podczas odbierania rozmowy) o o Program staje się aktywny Dokonać inicjalizacji, wczytać zasoby, pokazać coś na ekranie Jeśli nie udaje się uaktywnić, rzucić wyjątek MIDletStateChangeException Jeśli nieodwracalny błąd, wywołać notifyDestroyed Program staje się nieaktywny Zwolnić wszystkie zasoby zajmujące dużo procesora lub pamięci destroyApp() odpowiedzialnej za zamykanie aplikacji o o o Program zostaje wyłączony Zapisać trwałe dane Jeśli odmawia wyłączenia, rzucić wyjątek MIDletStateChangeException (tylko jeśli unconditional == false) Zatem szkielet najprostszego midletu powinien wyglądać następująco: import javax.microedition.midlet.MIDlet; public class PierwszyMIDlet extends MIDlet { public void startApp(){ } public void pauseApp(){ } public void destroyApp(boolean unconditional){ } } Wyświetlanie dowolnej informacji na ekranie można zrealizować za pomocą: obiektu TextBox i wstawieniu go na pulpit wyświetlacza. Konstruktor klasy TextBox przyjmuje trzy parametry: public TextBox(String title, String text, int maxSize, int constraints); tj. tytuł kontrolki komunikatu, treść komunikatu oraz długość i ewentualne ograniczenia Z kolei referencja Display do obiektu wyświetlacza jest dziedziczona po klasie MIDlet i referencję do pulpitu wyświetlacza można uzyskać korzystając z metody getDisplay. Następnie za pomocą metody setCurrent ustawiamy kontrolkę jako ekran bieżący np. tb = new TextBox("Tytuł", "Treść komunikatu", 80, TextField.UNEDITABLE); Display.getDisplay(this).setCurrent(tb); obiektu Alert Konstruktor klasy Alert przyjmuje 1 lub 4 parametry public Alert(String title, AlertType String alertText, Image alertImage, alertType) tj. tytuł alertu, jego komunikat, ewentulana referencja do obrazka i rodzaj. Dodatkowo można ustawić mu czas życia w milisekundach lub zdecydować, że ma być wyświetlany do czasu zamknięcia go przez użytkownika al.setTimeout(Alert.FOREVER); i podobnie wyświetlić go na ekranie. Poniżej zamieszczone są gotowe fragmenty kodu ilustrujące metody wyświetlania informacji. Wyświetlanie informacji na ekranie import javax.microedition.lcdui.*; import javax.microedition.midlet.MIDlet; public class PierwszyMIDlet extends MIDlet { // wykorzystanie klasy TextBox (okno tekstowe) private TextBox tb; public void startApp() { // tworzymy obiekt klasy TextBox tb = new TextBox("Tytuł", "Treść komunikatu", 80, TextField.UNEDITABLE); // ustawiamy ekran poczatkowy Display.getDisplay(this).setCurrent(tb); } public void pauseApp() { } public void destroyApp(boolean u) { } } Przykładowa aplikacja wykorzystująca klasę Alert import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class PierwszyMIDlet extends MIDlet { public void startApp() { Alert al = new Alert("Tytuł", "Treść informacji", null, AlertType.INFO); al.setTimeout(Alert.FOREVER); Display.getDisplay(this).setCurrent(al); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } } W dalszej części kursu zostanie pokazane obsługa komend w MIDletach, która związana jest z implementacją interfejsu CommandListener oraz definicją metody public void commandAction(Command c, Displayable d); Przykładowa aplikacja z obsługą komendę (zamykanie MIDletu) import javax.microedition.lcdui.*; import javax.microedition.midlet.MIDlet; public class WyswietlanieTekstu extends MIDlet implements CommandListener { private TextBox tb; private Command exitCommand; public void startApp() { tb = new TextBox("Tytuł", "Komunikat", 80, TextField.UNEDITABLE); // tworzymy nowe polecenie exitCommand = new Command("Zakończ", Command.EXIT, 0); // dodajemy polecenie do pola tekstowego tb.addCommand(exitCommand); // wskazujemy obiekt obslugujacy zdarzenia tb.setCommandListener(this); Display.getDisplay(this).setCurrent(tb); } public void pauseApp() { } public void destroyApp(boolean u) { } // ciało metody z interfejsu CommandListener public void commandAction(Command c, Displayable d) { // obsługa polecenia exitCommand (kończy działanie aplikacji) if (c == exitCommand) { destroyApp(true); notifyDestroyed(); } } } Telefon sam określa rozmieszczenie i sposób uruchamiania poleceń Kiedy poleceń jest dużo, zapewnia menu Część 2. Należy skorzystać z implementacji klasy Canvas oraz przesłonić jej kilka kluczowych metod. Wywołanie metody repaint() powoduje wymuszenie odrysowania ekranu i wywołanie metody paint(). import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class Wskaznik extends MIDlet{ private Display display; public void startApp(){ display = Display.getDisplay(this); display.setCurrent (new MyCanvas()); } public void pauseApp(){} public void destroyApp (boolean forced){} } class MyCanvas extends Canvas { String akcja = "Naciśnij"; int x; int y; public MyCanvas() { setFullScreenMode(true); } public void pointerPressed (int x, int y) { akcja = "Naciśnięty"; this.x = x; this.y = y; repaint (); } public void pointerReleased (int x, int y) { akcja = "Zwolniony"; this.x = x; this.y = y; repaint (); } public void pointerDragged (int x, int y) { akcja = "Przenoszenie"; this.x = x; this.y = y; repaint (); } public void paint (Graphics g) { g.setGrayScale (255); g.fillRect (0, 0, getWidth(), getHeight()); g.setGrayScale (0); g.drawString (akcja + " " + x + "/" + y, 0, 0, Graphics.TOP|Graphics.LEFT); g.drawLine (x-4, y, x+4, y); g.drawLine (x, y-4, x, y+4); } } Uwaga! Aby poprawnie działał w symulatorze ekran dotykowy, należy we właściowościach projektu w części Platform zmienić rodzaj symulowanego urządzenie. 1. Rysowanie po ekranie Do rysowania należy utworzyć własną klasę dziedzicząca po znanej już klasie Canvas i dodać jej instancję do obiektu Display. Metoda paint() odrysowuje ekran zgodnie z jej definicją. Przykład kodu: import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class RysowaniePoEkranie extends MIDlet{ private Display display; public void startApp(){ Canvas plotno = new Plotno(); display = Display.getDisplay(this); display.setCurrent(plotno); } public void pauseApp(){} public void destroyApp(boolean unconditional){} } class Plotno extends Canvas { public Plotno() { setFullScreenMode(true); } public void paint(Graphics g) { int width = getWidth(); int height = getHeight(); g.setColor(255, 162, 117); g.fillRect(0, 0, width, height); g.setColor(0, 0, 255); g.drawLine(0, height/2, width - 1, height/2); g.setStrokeStyle(Graphics.DOTTED); g.setColor(0xFFFF00); g.drawLine(0, height/4, width - 1, height/4); g.setColor(0, 0, 255); g.setStrokeStyle(Graphics.SOLID); g.drawLine(0, 0, width - 1, height - 1); } } Zadania: 1.Stworzyć midlet w którym użytkownik będzie miał możliwość wypełnienia kwestionariusza osobowego. Użytkownik powinien zobaczyć następujące pola: Imię – typ string; Nazwisko – typ string; Data urodzin – typ date; Płeć – dwa pola jednokrotnego wyboru; Stan cywilny – dwa pola jednokrotnego wyboru; Miejsce urodzenia – typ string; Numer telefonu – typ całkowity; Po wypełnieniu kwestionariusza aplikacja ma sprawdzić czy wszystkie pola zostały wypełnione i w zależności od rezultatu wyświetlić odpowiedni komunikat (alert) Jeśli jakieś pole niewypełnione: „proszę uzupełnić kwestionariusz”, alert powinien czekać na odpowiedź użytkownika (na wybranie przycisku ok.); Jeśli wszystkie wypełnione alert ma wyglądać następująco : „imię nazwisko, płeć stan cywilny, urodzony w miasto, w wieku wiek lat o numerze telefonu prawidłowo wypełnił kwestionariusz” Np.: „ Paweł Fiderek, mężczyzna żonaty, urodzony w Tomaszów, w wieku 25 lat o Umerze telefonu 555444111 prawidłowo wypełnił kwestionariusz” Uwaga! Ważne jest obliczenie wieku w latach na podstawie daty urodzin. Powodzenia!