Strona tytułowa
Tytuł: Czujniki, multimedia, gry i komunikacja - jak wykorzystać możliwości telefonu. Omawiamy zasoby, których nie ma zwykły komputer.
Tytuł: Czujniki, multimedia, gry i komunikacja - jak wykorzystać możliwości telefonu. Omawiamy zasoby, których nie ma zwykły komputer.
Prezentacja piąta pokazuje, dlaczego aplikacje mobilne są wyjątkowe: telefon ma czujniki, aparat, mikrofon, głośnik, łączność i ekran dotykowy. Wykorzystanie tych możliwości otwiera zupełnie nowe klasy aplikacji - od gier ruchowych po aplikacje fitness.
Na końcu prezentujemy autorski pomysł aplikacji, który łączy czujniki, zapis danych i elementy grywalizacji.
Prezentację warto przerabiać aktywnie: po każdym module dobrze jest uruchomić prosty przykład w App Inventor i sprawdzić odczyty czujników na prawdziwym telefonie.
Kluczowa różnica między aplikacją mobilną a programem działającym na komputerze to zestaw wbudowanych czujników i możliwość ich wykorzystania w czasie rzeczywistym. Telefon potrafi nie tylko przetwarzać dane wpisane przez użytkownika, lecz także samodzielnie zbierać informacje o otoczeniu oraz reagować na nie w ułamku sekundy. Dlatego projektowanie aplikacji mobilnej zaczyna się od zastanowienia, która z tych możliwości rozwiąże realny problem.
W trakcie cyklu zajęć student stopniowo przechodzi od pojedynczych komponentów do złożonego, autorskiego projektu końcowego. Istotne jest, aby każda nowa umiejętność była od razu sprawdzana w praktyce, najlepiej na fizycznym urządzeniu zamiast w emulatorze. Tylko w ten sposób można ocenić, jak czujniki zachowują się w rzeczywistych warunkach, na przykład podczas trzymania telefonu w ręku.
Spis treści został ułożony według naturalnej ścieżki poznawczej: najpierw czujniki i ich odczyty, potem multimedia, a następnie budowa gry i komunikacja. Kolejność ta powtarza sposób, w jaki powstaje realna aplikacja mobilna, czyli od zebrania danych aż do prezentacji wyników użytkownikowi. Dzięki temu pod koniec wykładu widać wyraźny związek między pozornie odległymi tematami prezentacji.
Warto zwrócić uwagę, że każda część planu odpowiada jednej kategorii komponentów w palecie App Inventor: Sensors, Media oraz Drawing and Animation. Takie porządkowanie sprzyja zapamiętywaniu, ponieważ uczeń uczy się jednocześnie struktury środowiska i logiki tworzenia aplikacji. Znajomość tej struktury bardzo ułatwia późniejsze samodzielne poszukiwanie komponentów.
Ostatnie pozycje spisu poświęcone są etyce oraz podsumowaniu całego kursu, co ma przypominać, że programista ponosi odpowiedzialność za to, jak jego aplikacja przetwarza dane. Student, który przejdzie przez wszystkie części planu, zyska komplet narzędzi potrzebnych do samodzielnej pracy nad własnym projektem. Z tego względu ten slajd warto traktować jako punkt odniesienia podczas każdej powtórki materiału.
Cele uczenia się zostały sformułowane zgodnie z zasadą opisującą, co konkretnie student potrafi zrobić po zakończeniu zajęć, a nie tylko, jakie treści poznał. Taki sposób formułowania celów ułatwia zarówno naukę, jak i weryfikację postępów, ponieważ każdy punkt można sprawdzić na działającej aplikacji. Warto w trakcie ćwiczeń samodzielnie próbować każdego z wymienionych zadań w środowisku projektowym.
Poziomy umiejętności W1-W5 dotyczą pojedynczych komponentów, natomiast cel K1 wymaga ich łączenia w spójny projekt. Przejście od znajomości pojedynczego czujnika do zbudowania kompletnej aplikacji to najważniejszy krok w nauce, gdyż odtwarza realną pracę programisty. Z tego powodu na zajęciach kładzie się nacisk właśnie na projekt autorski, który sprawdza wszystkie wcześniejsze kompetencje naraz.
Sformułowanie celów w takiej postaci ułatwia również ukierunkowanie samodzielnej nauki poza uczelnią. Student może porównać swoje postępy z listą i szybko zidentyfikować obszary wymagające dodatkowych ćwiczeń. Zapis celów na początku prezentacji sprawia też, że każda kolejna część wykładu ma jasno określone miejsce w całości kursu.
W aplikacjach komputerowych dane wejściowe pochodzą głównie z klawiatury i myszy, natomiast telefon dysponuje znacznie bogatszym zestawem źródeł informacji. Czujniki działają w sposób ciągły i nie wymagają od użytkownika żadnej akcji, dzięki czemu aplikacja może reagować na zmianę otoczenia w czasie rzeczywistym. To właśnie ta właściwość sprawia, że ta sama funkcja w wersji mobilnej wygląda zupełnie inaczej dla użytkownika.
Połączenie danych z różnych czujników daje efekty, których żaden z nich nie osiągnie samodzielnie, na przykład zestawione odczyty akcelerometru i lokalizacji umożliwiają rozpoznanie sposobu poruszania się z telefonem. Architektura programowa oparta na zdarzeniach doskonale pasuje do takiego modelu, gdyż każda zmiana wartości czujnika generuje zdarzenie, na które aplikacja może zareagować. Dla projektanta oznacza to, że powinien najpierw zastanowić się, jakich danych dostarczy czujnik, a dopiero potem projektować interfejs.
Dostęp do tych źródeł wymaga jednak świadomości zagadnień prywatności, ponieważ dane o ruchu i położeniu dotyczą bezpośrednio użytkownika. Twórca odpowiedzialnej aplikacji ogranicza zbieranie danych do niezbędnego minimum i informuje o tym użytkownika. Zrozumienie tej zasady jest równie ważne dla zawodu programisty jak umiejętność budowania bloków logicznych.
Sekcja Sensors w App Inventor gromadzi komponenty niewidoczne z perspektywy interfejsu, które w trakcie działania aplikacji dostarczają danych o urządzeniu i otoczeniu. Ich wspólną cechą jest to, że większość z nich wymaga fizycznego urządzenia, ponieważ emulator nie odwzorowuje zachowania czujników. Przed testowaniem każdej aplikacji opartej na sensorach warto zatem przygotować telefon lub tablet połączony z serwisem.
Obok akcelerometru, czujnika orientacji i lokalizacji w palecie znajdują się również dodatkowe sensory, takie jak barometr, termometr, czujnik światła czy czujnik odległości. Dostępność tych komponentów zależy od sprzętu, dlatego właściwość Available zawsze należy sprawdzić przed odczytem. Świadomość, że nie każde urządzenie ma wszystkie sensory, uczy projektowania aplikacji odpornych na brak danego czujnika.
Warto pamiętać, że komponentu Clock nie zalicza się ściśle do czujników fizycznych, lecz pełni on kluczową rolę, odmierza bowiem czas pomiędzy kolejnymi zdarzeniami. W praktyce łączy się go z czujnikami, aby nadawać odczytom odpowiednią częstotliwość. Zrozumienie roli każdego z tych komponentów z osobna jest pierwszym krokiem do budowy bardziej złożonych aplikacji.
Akcelerometr to czujnik mechaniczny, który mierzy przyspieszenie, czyli szybkość zmiany prędkości w trzech prostopadłych kierunkach. W spoczynku jedyna działająca na urządzenie siła to ciążenie ziemskie, dlatego w jednej z osi odczytujemy wartość bliską przyspieszeniu ziemskiemu. To właśnie ta zależność pozwala wykorzystać akcelerometr jako poziomicę, chociaż w sensie fizycznym mierzy on nie położenie, lecz siłę.
Przechylenie telefonu sprawia, że składowa grawitacji rozkłada się między osie, dzięki czemu na podstawie trzech liczb można określić, w jakiej pozycji znajduje się urządzenie. Duże wartości we wszystkich osiach oznaczają natomiast ruch z przyspieszeniem, na przykład energiczne szarpanie telefonem. Rozróżnienie tych dwóch sytuacji w kodzie sprowadza się do analizy znaków i wielkości odczytów.
Właściwość Sensitivity umożliwia dostrojenie czułości czujnika do rodzaju wykrywanego ruchu, a właściwość LegacyMode pozostaje dostępna dla starszych projektów. Wartości podawane w metrach na sekundę do kwadratu są liczbami zmiennoprzecinkowymi i bywają nieznacznie niestabilne, dlatego w praktyce zawsze wprowadza się progi tolerancji. Ustalanie właściwych progów to umiejętność, która przychodzi wraz z testowaniem rozwiązań na różnych urządzeniach.
Model zdarzeniowy App Inventor sprawia, że program nie pyta ciągle o wartość przyspieszenia, lecz czeka, aż czujnik sam zgłosi zmianę. Zdarzenie AccelerationChanged przekazuje trzy współrzędne w chwili, gdy ruch urządzenia uległ zmianie, co odzwierciedla sposób działania rzeczywistych aplikacji mobilnych. Taka konstrukcja jest wygodna, ale wymaga również pamiętania o tym, że zdarzenie może być wywoływane bardzo często.
Zanim odczytamy wartości czujnika, należy sprawdzić właściwość Available, aby aplikacja nie wykonywała operacji na nieistniejącym sprzęcie. Wiele błędów podczas testów wynika właśnie z pominięcia tego warunku, zwłaszcza w środowisku emulatora. Dobrą praktyką jest ponadto wyłączanie czujnika, gdy nie jest potrzebny, co ogranicza zużycie energii przez urządzenie.
Liczby z czujnika są surowymi danymi, które dopiero warunki i operacje matematyczne przekształcają w zrozumiałą reakcję aplikacji. Wykorzystanie wartości bezwzględnej, porównań i progów pozwala odróżnić celowy ruch od przypadkowych drgań. Umiejętność analizy danych z czujnika przydaje się także poza App Inventor, ponieważ w innych technologiach mobilnych stosuje się podobne wzorce.
Czujnik orientacji wyznacza położenie telefonu względem powierzchni ziemi i kierunków świata, podając trzy kąty wyrażone w stopniach. Roll opisuje przechył w lewo lub w prawo, Pitch pochylenie w przód lub w tył, a Azimuth kąt względem północy geograficznej. W ten sposób powstaje pełny opis tego, jak ułożone jest urządzenie w przestrzeni.
W przeciwieństwie do akcelerometru, który mierzy siłę, czujnik orientacji podaje bezpośrednio kąt, przez co sterowanie nim jest prostsze i bardziej przewidywalne. Warto zwrócić uwagę, że kąt Azimuth opiera się na polu magnetycznym Ziemi, zatem otoczenie z dużą liczbą metalowych elementów może zaburzać odczyt. Z tego powodu dokumentacja zaleca kalibrację kompasu przez wykonanie ruchu w kształcie ósemki.
Zdarzenie OrientationChanged dostarcza wartości dopiero w momencie zmiany położenia, co dobrze współgra z aplikacjami reagującymi na obrót. Dane można też odczytywać za pomocą właściwości, gdy potrzebna jest bieżąca wartość pojedynczego kąta. Czujnik ten bywa mylony z akcelerometrem, warto więc pamiętać różnicę: akcelerometr bada ruch, a czujnik orientacji określa ułożenie.
Kompas cyfrowy to najprostsze zastosowanie kąta Azimuth, który dla północy przyjmuje wartość zero i rośnie zgodnie z ruchem wskazówek zegara. Poziomicę budujemy natomiast dzięki obserwacji kątów Roll i Pitch, które przy idealnym położeniu poziomym są równe zeru. Tego typu narzędzia dobrze pokazują, jak surowa wartość z czujnika zamienia się w praktyczną funkcję użytkową.
W grach wyścigowych i zręcznościowych kąt pochylenia zastępuje wirtualny ster, odczytujemy go i przeliczamy na ruch obiektu na ekranie. Połączenie czujnika orientacji z animacją pozwala uzyskać naturalne wrażenie prowadzenia pojazdu, którego nie zapewni dotykanie przycisków. Takie rozwiązanie jest możliwe tylko w aplikacji mobilnej i stanowi dobrą ilustrację przewagi telefonu nad komputerem.
Wykrywanie położenia płaskiego, na przykład podczas odłożenia telefonu na stół, pozwala automatycznie zmienić tryb aplikacji albo wstrzymać pomiary. Ta sama wartość umożliwia też obracanie interfejsu zgodnie z ułożeniem ekranu, chociaż w codziennych zastosowaniach system zwykle zajmuje się tym automatycznie. Znając znaczenie poszczególnych kątów, projektant może elastycznie dostosowywać zachowanie programu do pozycji urządzenia.
Zdarzenie Shaking pozwala obsłużyć energiczny ruch telefonem bez dotykania ekranu, co w wielu sytuacjach bywa wygodniejsze od naciskania przycisku. Jego działanie opiera się na nagłym wzroście wartości przyspieszenia w jednej z osi, który przekracza próg uznany za potrząśnięcie. W kodzie nie musimy samodzielnie analizować tych wartości, ponieważ czujnik zgłasza samo zdarzenie.
Właściwość MinimumInterval określa minimalny odstęp między kolejnymi zdarzeniami Shaking, wyrażony w milisekundach. Dzięki niej aplikacja nie wykonuje losowania wielokrotnie podczas jednego potrząśnięcia, co bywa szczególnie uciążliwe przy symulacji rzutu kostką. Odpowiednio dobrany interwał pozwala więc zachować pełną kontrolę nad częstotliwością reakcji.
Mechanizm potrząśnięcia dobrze nadaje się do akcji losowych, odtwarzania losowego hasła albo przełączania widoku, jednak nie zastąpi dokładnego sterowania ruchem. Tam, gdzie potrzebna jest precyzja, należy użyć pełnych odczytów z danej osi zamiast wyzwalania samego zdarzenia. Łączenie obu sposobów sprawdza się w grach, w których jedno potrząśnięcie wyzwala skok, a delikatne pochylenie kieruje postacią.
Ćwiczenie polega na zbudowaniu miniaturowej poziomicy, która na podstawie jednej osi akcelerometru informuje o kierunku przechyłu. Taki uproszczony przykład świetnie pokazuje całą drogę od odczytu surowej wartości do podjęcia decyzji w kodzie. Ograniczenie się do jednej osi ułatwia zrozumienie mechanizmu, zanim student przejdzie do bardziej złożonych sterowań.
Kluczowym elementem jest ustalenie progu, czyli wartości odcięcia, poniżej której drgania telefonu nie są traktowane jak przechylenie. Bez tego zabiegu etykieta migałaby ciągle między komunikatami, co znacznie utrudniłoby odczyt wyniku. Dobór progu wymaga praktycznego testowania, dlatego to ćwiczenie warto wykonać na prawdziwym telefonie.
Warunki logiczne w tym zadaniu są prostą ilustracją klasycznej struktury decyzyjnej, która pojawia się niemal w każdej aplikacji. Utrwalenie tego wzorca procentuje w dalszej części kursu, gdzie warunki sterują przebiegiem gier i logiką zachowań. Warto także zwrócić uwagę, że same komunikaty można dowolnie rozbudować, na przykład o sygnał dźwiękowy lub zmianę kolorów.
System GPS wyznacza położenie na podstawie sygnałów z satelitów, dlatego działa najlepiej w otwartej przestrzeni i znacznie gorzej wewnątrz budynków. Właściwość Accuracy informuje o promieniu niepewności położenia, co pozwala aplikacji ocenić, czy dany odczyt można uznać za pewny. Porównanie dokładności kolejnych odczytów jest podstawą poprawnego przetwarzania danych o lokalizacji.
Komponent LocationSensor pobiera położenie z kilku źródeł, między innymi z GPS oraz lokalizacji po sieciach Wi-Fi i stacjach telefonicznych, i dostarcza szerokość, długość, wysokość oraz prędkość. Konieczne jest włączenie lokalizacji w ustawieniach urządzenia oraz wyrażenie zgody przez użytkownika, zanim dane staną się dostępne. System operacyjny traktuje lokalizację jako dane wrażliwe, dlatego zgoda jest pobierana w sposób jawny.
Pierwszy odczyt może być opóźniony, gdyż urządzenie musi najpierw ustalić kontakt ze źródłem sygnału, dlatego aplikacja powinna informować o trwającym pomiarze. Wiele kłopotów z lokalizacją wynika właśnie z braku cierpliwości podczas inicjalizacji czujnika. Dobre projekty przewidują stan, w którym położenie nie jest jeszcze znane, i reagują czytelnym komunikatem.
Dane geograficzne w naturalny sposób nadają się do aplikacji rejestrujących trasę, aktywność oraz miejsca odwiedzane przez użytkownika. Liczenie przebytego dystansu wymaga zapamiętania poprzedniego położenia i dodania do sumy odległości między kolejnymi odczytami. W ten sposób prosty licznik przekształca się w narzędzie, które zna całkowity przebieg wędrówki.
Zapis tras w bazie TinyDB umożliwia późniejsze rysowanie ich na mapie, a także porównywanie aktywności w kolejnych dniach. Dodanie współrzędnych do zdjęć, czyli tzw. geotagging, wzbogaca notatki o kontekst miejsca i staje się fundamentem wielu aplikacji dokumentacyjnych. Przetwarzanie list punktów to także dobre ćwiczenie z operowania strukturami danych.
Powiadomienia o dotarciu do celu wymagają ciągłego porównywania bieżącej pozycji z punktem docelowym i sprawdzania odległości. Ponieważ odczyty nie zawsze są stabilne, warto używać promienia tolerancji zamiast porównania do zera. Takie podejście uczy ważnej zasady inżynierskiej, że dane ze świata rzeczywistego zawsze obarczone są pewnym błędem.
Zdarzenie LocationChanged to naturalne miejsce, w którym aplikacja reaguje na zmianę położenia i aktualizuje informacje pokazywane użytkownikowi. Wartości szerokości i długości geograficznej można łączyć z tekstem za pomocą bloku łączenia, co jest typowym przykładem operacji na napisach. Taka konstrukcja wymaga jednak pamiętania o kolejności składowych, aby komunikat pozostał czytelny.
Właściwość HasLongitudeLatitude chroni przed wyświetlaniem pustych odczytów, które pojawiłyby się przed pierwszym poprawnym pomiarem. Ogólnie rzecz biorąc, każdy odczyt czujnika należy poprzedzić sprawdzeniem, czy dane są w ogóle dostępne. Ta sama reguła dotyczy wysokości i prędkości, które na części urządzeń w ogóle nie występują.
Zapis punktów trasy do TinyDB w stałym odstępie czasu tworzy bazę, z której można odtworzyć przebytą ścieżkę. Dobór interwału ma realne konsekwencje: rzadkie zapisy dają trasę uproszczoną, a częste mogą szybko wypełnić pamięć. Zrozumienie tego kompromisu jest elementem umiejętności projektowania praktycznych systemów pomiarowych.
Komponent Camera odgrywa rolę swoistego mostu między aplikacją a systemowym aparatem, ponieważ samo wykonanie zdjęcia przekazywane jest zewnętrznemu programowi. Po powrocie z aparatu zdarzenie AfterPicture dostarcza ścieżkę do pliku, który można wyświetlić lub przetwarzać dalej. Taki podział zadań oznacza, że aplikacja nie musi sama obsługiwać sprzętu fotograficznego, a jedynie oczekiwać na efekt.
Systemowe zarządzanie dostępem sprawia, że pierwsze uruchomienie aparatu wiąże się z pytaniem o zgodę, co jest standardem ochrony prywatności. Komponent ImagePicker stanowi alternatywę dla robienia nowych zdjęć, pozwala bowiem wybrać istniejący obraz z galerii. Wybór między oboma rozwiązaniami zależy od scenariusza użycia i od tego, czy potrzebne jest zdjęcie świeże czy już wcześniej zapisane.
Zdjęcie zapisane w pamięci urządzenia można dalej umieszczać w komponentach graficznych, opisywać w bazie danych albo udostępniać innym aplikacjom. Należy jednak pamiętać, że ścieżka pliku jest tylko tekstem i po zmianach w systemie może być nieaktualna. Użycie tych możliwości w połączeniu z lokalizacją prowadzi do bogatych aplikacji dokumentacyjnych.
Projekt notatnika łączy obraz z tekstem i demonstruje trwałe przechowywanie obu tych elementów w bazie TinyDB. Wystarczy zapamiętać opis razem ze ścieżką do pliku zdjęcia, aby później odtworzyć całą notatkę na ekranie. Ten wzorzec pary danych pojawia się w wielu realnych aplikacjach, między innymi w dziennikach i ewidencjach.
Komponent ListPicker umożliwia wygodny wybór zapisanej notatki spośród wielu wpisów, a jego obsługa sprowadza się do zdarzenia AfterPicking. W kodzie należy odczytać wybraną wartość i na jej podstawie pobrać z bazy pozostałe informacje. Zrozumienie tego przepływu danych między listą a szczegółami uczy projektowania typowych ekranów nawigacyjnych.
Dodanie współrzędnych GPS do każdej notatki wzbogaca aplikację o kontekst miejsca i może być punktem wyjścia do projektów opartych na geolokalizacji. Rozszerzanie prostego notatnika o kolejne funkcje pokazuje też, jak buduje się aplikacje od wersji podstawowej do pełniejszej. Taki sposób stopniowego rozwoju stanowi dobre wzorcowe podejście do pracy projektowej.
W App Inventor dźwięk obsługują trzy komponenty o różnych przeznaczeniach: Player, Sound oraz SoundRecorder. Komponent Player przeznaczony jest do długich plików, takich jak muzyka lub nagrania, i udostępnia pełną kontrolę przez metody Start, Pause i Stop. Komponent Sound gra natomiast krótkie efekty, które mają wzmacniać interakcję i które nie wymagają pauzowania.
Pliki dźwiękowe można dodać do projektu jako zasoby w sekcji Media albo odwoływać się do plików zapisanych na urządzeniu. W przypadku nagrań własnych, wykonywanych komponentem SoundRecorder, trzeba uprzednio wskazać ścieżkę docelową zapisu. Obsługa uprawnień do mikrofonu stanowi odpowiednik zgód na aparat i lokalizację.
Efekty dźwiękowe istotnie podnoszą jakość odbioru aplikacji, dlatego nawet proste potwierdzenia i sygnały warto dodawać do interfejsu w sposób przemyślany. Należy jednak unikać nadmiaru dźwięków, które bywają denerwujące i utrudniają korzystanie z urządzenia w miejscach publicznych. Projektant zajmujący się udźwiękowieniem powinien jednocześnie przewidzieć możliwość wyłączenia wszystkich efektów.
Rozpoznawanie mowy polega na zamianie sygnału dźwiękowego z mikrofonu na tekst, co wykonuje silnik dostępny w systemie urządzenia. Właśnie dlatego komponent SpeechRecognizer w dużej mierze opiera się na zasobach systemowych i zwykle potrzebuje połączenia z siecią, aby działać poprawnie. Podobną zależność widać w innych technologiach, gdzie asystentów głosowych udostępnia dostawca systemu lub serwis internetowy.
Metoda GetText rozpoczyna nasłuch, a gdy wypowiedź się zakończy, program otrzymuje rozpoznany tekst w zdarzeniu AfterGettingText. Na jakość rozpoznania wpływają takie czynniki, jak wyrazistość wymowy, poziom hałasu oraz mikrofon użytego urządzenia. Projektant aplikacji powinien przewidzieć, że użytkownik może powtórzyć komendę, i łagodnie obsłużyć brak wyniku.
Sterowanie głosem sprawdza się przede wszystkim w sytuacjach, w których ręce są zajęte, na przykład podczas prowadzenia samochodu albo pracy w kuchni. Z tego względu proste komendy o wyraźnie różnych brzmieniach dają znacznie lepsze efekty niż długie polecenia. Utrwalanie wzorca zapytania i odpowiedzi przygotowuje też grunt pod bardziej zaawansowane aplikacje konwersacyjne.
Połączenie komponentów TextToSpeech i SpeechRecognizer tworzy zamkniętą pętlę komunikacyjną: aplikacja mówi, użytkownik odpowiada, a program reaguje na treść odpowiedzi. W ten sposób powstaje wrażenie rozmowy, które stanowi podstawę asystentów głosowych i aplikacji edukacyjnych. Współpraca obu komponentów wymaga starannego zaplanowania kolejności operacji, aby nasłuch i mówienie nie nakładały się na siebie.
Porównywanie rozpoznanego tekstu z oczekiwaną odpowiedzią nie zawsze daje idealny wynik, dlatego w praktyce dopuszcza się drobne różnice i formułuje pytania tak, aby odpowiedzi były krótkie. Blok porównujący napisy dokonuje dokładnego porównania, co dla dłuższych fraz bywa zawodne. Dobra budowa pytań to umiejętność projektowania interfejsu głosowego, podobna do pracy nad parametrami w kodzie.
Taki wzorzec pętli można rozbudować o listę pytań, punktację i poziomy trudności, otrzymując kompletny quiz głosowy. Wykorzystanie mowy zmienia charakter ćwiczeń i zwiększa atrakcyjność aplikacji, zwłaszcza w nauce języków obcych. Projekt tego typu pokazuje, jak kombinacja prostych komponentów tworzy funkcję o dużej wartości użytkowej.
Ćwiczenie prowadzi krok po kroku do zbudowania aplikacji sterowanej dwoma komendami głosowymi, co jest najprostszym możliwym wzorcem sterowania. W jego trakcie student poznaje strukturę zdarzenia AfterGettingText oraz sposób przekształcania rozpoznanego napisu w decyzję. Taki minimalny przykład pozwala skupić się na samej logice, zanim komendy staną się bardziej złożone.
Porównania wykonywane są z dokładnym dopasowaniem tekstu, dlatego komendy powinny być zaprojektowane tak, aby system rozpoznawał je powtarzalnie. Wyświetlanie potwierdzenia na ekranie oraz dodatkowe powtórzenie wyboru głosem umożliwia użytkownikowi natychmiastową weryfikację. Dzięki temu aplikacja staje się bardziej przyjazna, nawet jeśli rozpoznanie nie było doskonałe.
To samo ćwiczenie można wzbogacić o dodatkowe reakcje, na przykład zmianę koloru tła, dźwięk lub animację, co wzmacnia zarazem wiedzę o innych komponentach. Dobór interfejsu do sposobu sterowania jest cenną lekcją projektowania, ponieważ komendy głosowe eliminują konieczność dotykania ekranu. Utrwalone tu wzorce przydają się później w grach i aplikacjach asystenckich.
Multimedia poszerzają zakres aplikacji o odtwarzanie filmów, nagrywanie wideo, tłumaczenie tekstu oraz pobieranie danych z internetu. Komponent VideoPlayer wymaga plików w formatach obsługiwanych przez urządzenie, zwykle .mp4, natomiast Camcorder przekazuje nagranie przez zdarzenie AfterRecording. Ważne jest, aby w projekcie przewidzieć właściwe uprawnienia do urządzeń zewnętrznych i pamięci.
Komponent Translator wykorzystuje zewnętrzny, chmurowy silnik tłumaczenia maszynowego i dlatego wymaga dostępu do sieci. Podobnie komponent Web umożliwia komunikację z serwerami, od odczytania strony po pobieranie danych w formacie JSON. Korzystanie z tych możliwości uczy studenta integracji aplikacji z usługami zewnętrznymi.
Obszar Canvas zamyka zestaw multimedialny jako miejsce własnego rysowania i animacji, stanowiące podstawę gier. Warto dostrzec, że poszczególne elementy multimediów można łączyć ze sobą, na przykład odtwarzać dźwięk podczas animacji albo tłumaczyć pobrany tekst. W ten sposób jedno środowisko daje komplet narzędzi do budowy zróżnicowanych aplikacji.
Obszar Canvas to dwuwymiarowa powierzchnia rysowania, na której każda pozycja opisana jest dokładnie parą współrzędnych X i Y, liczonych od lewego górnego rogu. Rysowanie wykonują metody wywoływane w blokach, takie jak DrawLine, DrawCircle i DrawText, które natychmiast modyfikują zawartość obszaru. W przeciwieństwie do zwykłych komponentów wygląd obrazu zależy tu wyłącznie od działania kodu aplikacji.
Aparat rysujący ma własne właściwości: ustala się bowiem kolor pędzla oraz grubość linii, a także rozmiar tekstu. Zdarzenia dotykowe obszaru dostarczają współrzędnych, dzięki czemu łatwo obsłużyć rysowanie palcem bezpośrednio na ekranie. Właśnie ta prostota czyni z Canvas najważniejszy element dla twórców gier i interaktywnych widżetów.
Prostokąt na obszarze rysuje się metodą DrawShape, która przyjmuje listę punktów wierzchołkowych, zatem każda figura ograniczona łamaną powstaje w podobny sposób. Dodatkowe metody pozwalają zapisywać zawartość obszaru do pliku i odczytywać kolory pojedynczych pikseli. Znajomość tych możliwości otwiera drogę do tworzenia wykresów, plansz i interfejsów rysowanych w pełni w kodzie.
Sprity, czyli komponenty Ball i ImageSprite, to jedyne elementy mogące poruszać się po obszarze Canvas bez pomocy dodatkowych metod. Ruch uruchamia się przez ustawienie prędkości i kierunku, po czym sam komponent aktualizuje swoją pozycję w ustalonych odstępach czasu. Dzięki temu nie trzeba ręcznie przesuwać obiektu w każdej klatce, co upraszcza logikę gier.
Ball to kula, której wygląd tworzy promień i kolor, natomiast ImageSprite czerpie grafikę z pliku obrazka i może się obracać zgodnie z kierunkiem. Obie postaci mają wspólne zdarzenia, takie jak zderzenie, dotknięcie czy osiągnięcie krawędzi obszaru. Wybór między kulą a duszkiem obrazkowym zależy wyłącznie od tego, jak ma wyglądać postać.
Metody MoveTo, MoveIntoBounds i Bounce dają pełną kontrolę nad pozycją i odbiciami, a zdarzenie EdgeReached zwraca numer krawędzi, od której nastąpiło odbicie. Sprity same w sobie realizują prostą animację ruchu, co idealnie łączy się z wykrywaniem kolizji. Zrozumienie ich działania jest fundamentem każdej gry zręcznościowej w App Inventor.
Zegar pozwala sterować animacją w sposób niezależny od wewnętrznego ruchu sprite'ów, choć oba podejścia często się ze sobą łączą. Zdarzenie Timer wywoływane jest cyklicznie co ustalony interwał i w tym momencie program może zmienić położenie dowolnego obiektu. Taka architektura daje pełną kontrolę nad tempem animacji oraz umożliwia aktualizację stanu gry w jednym miejscu kodu.
Zmniejszenie interwału sprawia, że kolejne kroki ruchu następują częściej i animacja wydaje się płynniejsza, ale jednocześnie rośnie obciążenie urządzenia. W praktyce dobiera się kompromis między płynnością a zużyciem energii, zwykle przy interwale rzędu kilkudziesięciu milisekund. Warto również pamiętać, że system nie gwarantuje dokładnego zachowania czasu, gdy urządzenie jest zajęte.
Ruch w górę, w dół lub po skosie uzyskuje się przez modyfikację współrzędnych obiektu o stały krok w każdej iteracji. Odbicie od krawędzi można zaimplementować przez zmianę znaku składowej ruchu albo wykorzystanie metody Bounce. Umiejętność łączenia zegara z innymi komponentami leży u podstaw większości mobilnych gier zręcznościowych.
Wykrywanie zderzeń to mechanizm, dzięki któremu gra wie, że dwa obiekty znalazły się w tym samym miejscu obszaru. Zdarzenie CollidedWith dostarcza informację o drugim obiekcie, dzięki czemu można odróżnić spotkanie z nagrodą od spotkania z przeszkodą. Ta zdolność rozróżniania decyduje o całej mechanice rozgrywki.
Porównanie parametru other z konkretnym komponentem pozwala zastosować różne reakcje, na przykład dodać punkt za zderzenie z monetą, a zakończyć grę po kontakcie z bombą. Taki sposób programowania zdarzeń odpowiada tworzeniu zachowań zależnych od rodzaju obiektu. Dzięki temu pojedynczy szkielet logiki obsługuje wiele sytuacji w grze.
Należy pamiętać, że kolizje są wykrywane tylko dla włączonych obiektów Sprite, a w przypadku obróconych grafik dokładność sprawdzenia może być ograniczona. Projekt planszy powinien zatem unikać bardzo wąskich lub bardzo płytkich przeszkód, które utrudniałyby wykrycie zderzenia. Zrozumienie tych ograniczeń zapobiega wielu trudnym do wykrycia błędom podczas testów.
Zdarzenia dotykowe obszaru Canvas dostarczają pełnej informacji o interakcji palca z ekranem, w tym o współrzędnych dotknięcia oraz o przebiegu przeciągania. Dzięki temu aplikacja może reagować nie tylko na kliknięcia, lecz również na gesty, co otwiera nowe możliwości projektowania interfejsu. Współrzędne liczone są od lewego górnego rogu obszaru, co ułatwia ich bezpośrednie użycie.
Przesunięcie obiektu w miejsce dotknięcia to najprostszy sposób sterowania, natomiast obsługa przeciągania pozwala realizować rysowanie linii i przesuwanie elementów planszy. Zdarzenie Dragged przekazuje także położenie startowe gestu, dzięki czemu łatwo wykryć kierunek ruchu. Ten sam mechanizm można wykorzystać do przesuwania sprite'ów zgodnie z ruchem palca.
Dotyk jako forma interakcji jest naturalna dla urządzeń mobilnych i zupełnie inna od myszy, dlatego przemyślane projektowanie gestów znacząco podnosi jakość aplikacji. Warto przy tym pamiętać o mniejszej precyzji dotyku niż przy klawiaturze i zapewnić odpowiednie rozmiary celów dotykowych. Wzorce wypracowane na obszarze Canvas pojawiają się również w bardziej zaawansowanych narzędziach deweloperskich.
Planowanie gry zaczyna się od jednoznacznego określenia celu, który gracz ma osiągnąć, oraz warunków końca rozgrywki. Każda mechanika, od sterowania po kolizje, wynika bezpośrednio z tych podstawowych założeń. Dobrze zdefiniowana specyfikacja ułatwia późniejszy podział pracy na mniejsze, wykonalne etapy.
Wybór sposobu sterowania determinuje zestaw komponentów, na przykład przechylenie wymaga akcelerometru, a dotyk zdarzeń obszaru rysowania. Ten sam wybór wpływa na odbiór gry, ponieważ sterowanie ruchem cechuje się innym charakterem niż precyzyjne kliknięcia. Ustalenie tego elementu wcześnie chroni przed koniecznością przerabiania całej architektury.
Punktacja, ograniczenie czasu i poziom trudności tworzą razem mechanizm motywujący gracza i nadający grze rytm. Stopniowe przyspieszanie lub dodawanie przeszkód sprzyja utrzymaniu zaangażowania bez wprowadzania nagłych skoków trudności. Taki dokument planistyczny, nawet w formie kilku zdań, służy potem jako punkt odniesienia podczas całej implementacji.
Proste gry oparte na App Inventor dzielą się na kilka typów: zręcznościowe, logiczne, pamięciowe oraz te wykorzystujące ruch urządzenia. Każdy z tych typów wymaga odrobiny innego zestawu komponentów i innej logiki, dlatego warto zbudować kilka z nich przed własnym projektem. Dzięki temu student zdobywa uniwersalne wzorce, które później bez trudu adaptuje.
Gra w łapanie piłek, labirynt czy prosty refleks uczy jednej, bardzo konkretnej mechaniki, co czyni ją znakomitym materiałem do nauki. Złożenie kilku takich mechanik w całość, na przykład sterowania ruchem i zbierania nagród, prowadzi do gry o bogatszej rozgrywce. Z tego powodu przegląd gotowych gier pełni rolę katalogu rozwiązań.
W projektach edukacyjnych duże znaczenie ma również zwięzłość kodu, która ułatwia jego analizę i przerabianie. Każda z prezentowanych gier może stać się podstawą własnej wersji po zmianie grafik, celów lub parametrów trudności. Rozwijanie gry przez modyfikację istniejącego rozwiązania to częsta praktyka w rzeczywistych zespołach deweloperskich.
Gra łączy w sobie wszystkie kluczowe mechanizmy poznane wcześniej w kursie, dlatego stanowi naturalne podsumowanie części praktycznej. Cel polegający na łapaniu spadających piłek do kosza tworzy czytelną i łatwą do zrozumienia regułę rozgrywki. Taka prostota założeń pozwala skupić większość uwagi na technice implementacji poszczególnych zachowań.
Sterowanie przechyłem zastępuje przyciski, co jest możliwe dzięki akcelerometrowi i dodaje grze charakterystyczny dla mobilnych aplikacji wymiar rozgrywki fizycznej. W projekcie jednocześnie pojawiają się animacja, kolizje, punktacja i stopniowe przyspieszanie, czyli wszystkie elementy wymieniane przy okazji planowania gier. Ta wzajemna współpraca komponentów dowodzi, że pojedyncze umiejętności składają się na spójny system.
Zbudowanie tej gry do końca wymaga systematycznej pracy, dlatego kolejne slajdy rozkładają ją na pojedyncze kroki. Taki podział odpowiada praktyce profesjonalnej, w której duże zadanie dzieli się na mniejsze fragmenty możliwe do wdrożenia i przetestowania. Po zakończeniu projektu student dysponuje kompletem bloków nadających się do modyfikacji na własne potrzeby.
Krok pierwszy koncentruje się na przygotowaniu zasobów projektu, czyli dodaniu grafik, dźwięków i ustawieniu podstawowych parametrów ekranu. Postać piłki oraz kosz do jej łapania to elementy, które w drugiej części zostaną powiązane z logiką ruchu. Taki porządek realizacji ogranicza liczbę czynników mogących zakłócić działanie aplikacji w trakcie budowy.
Umieszczenie komponentów na ekranie projektantom zazwyczaj nie stwarza trudności, jednak znaczenie ma spójne nazewnictwo wszystkich elementów. Czytelne nazwy ułatwiają odnajdywanie komponentów w długiej liście bloków i zmniejszają ryzyko pomyłek podczas programowania. Ta prosta zasada przydaje się tym bardziej, im bardziej rozbudowany staje się projekt.
Same zasoby graficzne można przygotować w dowolnym edytorze grafiki rastrowej albo pobrać z bibliotek darmowych materiałów. W App Inventor należy pamiętać o odpowiednim rozmiarze plików, gdyż zbyt duże obrazy spowalniają działanie aplikacji. Uporządkowane zasoby stanowią fundament, na którym w kolejnych krokach powstanie cała mechanika gry.
Po zbudowaniu wyglądu ekranu przychodzi czas na programowanie ruchu, które nadaje projektowi charakter gry. Sterowanie koszem opiera się na zdarzeniu akcelerometru, w którym wartość z jednej osi przeliczana jest na zmianę położenia sprite'a. Połączenie wartości czujnika z ruchem obiektu stanowi powtórzenie i utrwalenie materiału omawianego przy czujnikach ruchu.
Ruch ograniczony do płaszczyzny poziomej wymaga odwołania wyłącznie do jednej osi przyspieszenia i nadania jej odpowiedniego znaku. Dzięki właściwościom Width i Height obszaru rysowania można sprawić, aby kosz nigdy nie opuścił planszy. Taki mechanizm pilnowania granic jest nieodzowny w większości gier i warto utrwalić go razem z obsługą kolizji.
Warto od razu zaprogramować również informację o pierwszych zmianach wyniku, aby testy odzwierciedlały pełną ścieżkę rozgrywki. Włączenie elementów zarządzania grą we wczesnym etapie ułatwia zauważanie zależności między odbiorem a punktacją. Systematyczne budowanie funkcji w małych porcjach pozwala szybciej wykryć błędy i rozumieć ich przyczyny.
Spadanie piłki napędza zdarzenie Timer, które co kilkadziesiąt milisekund zwiększa współrzędną Y obiektu o ustalony krok. Im krótszy interwał ustawimy na zegarze, tym płynniejszy wydaje się ruch, ale też tym więcej obliczeń wykonuje urządzenie. Ważne jest więc dobranie interwału, który da płynną animację bez nadmiernego obciążenia telefonu.
Gdy piłka przekroczy dolną krawędź Canvas, należy ją przenieść na szczyt ekranu i ustawić w losowej kolumnie. Do wyznaczenia kolumny służy blok random integer, który losuje liczbę z zakresu między zerem a szerokością obszaru gry. Dzięki temu piłki nie spadają ciągle w to samo miejsce, a rozgrywka pozostaje nieprzewidywalna.
Warunek porównujący pozycję piłki z wysokością Canvas to typowy przykład pilnowania granic, który w grach stosuje się dla każdego obiektu ruchomego. W ten sam sposób można później ograniczać ruch kosza, aby nie wychodził poza ekran. Utrwalenie tego wzorca ułatwia rozbudowę gry o kolejne piłki i przeszkody.
Warstwa kolizji używa zdarzenia CollidedWith, aby rozpoznać moment złapania piłki do kosza i wykonać odpowiednie reakcje programu. Porównanie parametru other pozwala stwierdzić, czy piłka trafiła w kosz, po czym można dodać punkt do licznika. Każdy taki przebieg zdarzeń stanowi czytelną ilustrację komunikacji między komponentami projektu.
Po odliczeniu punktu nowa piłka pojawia się w losowym miejscu, dlatego w grze wykorzystuje się generator liczb losowych do ustalenia współrzędnych. Losowość wpływa na powtarzalność rozgrywki, utrudniając graczowi przewidzenie kolejnego zdarzenia i podnosząc atrakcyjność zabawy. W tym samym miejscu kodu można też zaktualizować wyświetlany wynik, aby gracz widział swoje postępy.
Obsługę kolizji warto rozbudować tak, aby przypadkowe dotknięcie brzegu obszaru nie fałszowało rezultatu rozgrywki. W praktyce wystarczy umieścić odpowiednie warunki wewnątrz zdarzenia i sprawdzić je, zanim wykonana zostanie akcja. Precyzyjne warunki to typowa umiejętność kształcona podczas budowy gier, przydająca się również w innych aplikacjach.
Zmienna licząca punkty przechowuje stan rozgrywki i w każdej iteracji zdarzenia ulega zmianie, dlatego dodatkowo zapisujemy jej wartość w bazie danych. Dzięki takiemu rozwiązaniu wynik gracza przetrwa nawet zakończenie aplikacji i będzie dostępny po kolejnym uruchomieniu. Trwałe przechowywanie stanu to cecha, która odróżnia pełnoprawne gry od prostych ćwiczeń.
Ekran po zakończeniu gry powinien pokazywać uzyskany wynik oraz rozpoczęcie kolejnej rozgrywki, co zamyka pełną pętlę rozgrywki. Zapis do TinyDB wykonuje się w momencie zakończenia partii bądź tuż po odliczeniu każdego punktu, w zależności od przyjętego projektu. Dodatkowo w ponownym uruchomieniu aplikacji wyniki z bazy mogą posłużyć do porównania z nową partią.
Zastosowanie bazy danych we własnej grze przybliża studenta do realiów tworzenia aplikacji, gdzie trwały zapis jest naturalną częścią projektu. Sensownym ćwiczeniem jest dodanie listy najlepszych wyników, co wymaga operowania strukturami danych. W ten sposób prosta gra staje się narzędziem kształcącym umiejętności przydatne w dalszej części kursu.
Prosty sposób na zwiększenie trudności polega na stopniowym przyspieszaniu piłki, na przykład co dziesięć zdobytych punktów. Wystarczy w jednym miejscu kodu zwiększać zmienną przechowującą krok ruchu, a wszystkie piłki od razu zaczną spadać szybciej. Dzięki takiemu rozwiązaniu poziom gry rośnie płynnie i nie wymaga dodawania nowych bloków.
Inne metody utrudnienia to uruchomienie drugiej piłki, zmniejszenie kosza albo dodanie przeszkód kończących rozgrywkę po zderzeniu. Każda z tych zmian korzysta z już poznanych mechanizmów, takich jak kolizje czy losowanie pozycji. Wprowadzanie nowych elementów stopniowo, po jednym na raz, ułatwia testowanie wpływu każdej zmiany na przebieg gry.
Balansowanie trudności wymaga obserwacji rzeczywistej rozgrywki i warto wykonać je na prawdziwym telefonie. Zbyt szybka piłka zniechęci początkującego gracza, a zbyt wolna szybko się znudzi. Celowe dobieranie tempa gry to umiejętność projektanta, która decyduje o tym, czy gra sprawia odpowiednią frajdę.
Budowa gry "Łap piłkę" pokazała, że czujnik, zegar, kolizje i zmienne wystarczą, aby powstała pełna rozgrywka. Każdy z tych elementów pełni jedną, jasno określoną rolę: akcelerometr steruje, zegar animuje, zdarzenie CollidedWith przyznaje punkty, a zmienne prowadzą stan gry. Zrozumienie tego podziału ułatwia tworzenie dowolnej innej gry zręcznościowej.
Zapis najlepszego wyniku w bazie TinyDB sprawia, że osiągnięcie gracza przeżywa zamknięcie aplikacji. Ten sam mechanizm można rozszerzyć o listę najlepszych wyników albo historię rozgrywek. Dzięki temu prosta gra przekształca się w aplikację z trwałym zapisem danych.
Najważniejszym wnioskiem jest to, że rozbudowany projekt powstaje z wielu prostych kroków wykonywanych po kolei. Każdy moduł gry był budowany i testowany osobno, zanim dołączono kolejny. Taki sposób pracy ogranicza liczbę błędów i sprawia, że gotowa gra jest łatwiejsza w modyfikowaniu.
Własne gry można budować, zmieniając tylko sposób sterowania i cel rozgrywki. Labirynt sterowany pochyleniem używa czujnika orientacji zamiast akcelerometru i sprawdza kolizje ze ścianami planszy. Gra refleks natomiast opiera się na zdarzeniu dotknięcia Canvas i wymaga szybkiego porównywania kolorów pól.
Gra pamięciowa z odwracaniem kart przechowuje stan kart w listach i porównuje obrazki po odkryciu. Kompas-skarb czyta kąt Azimuth i porównuje go z kierunkiem do celu, a skok bohatera wyzwala energiczne potrząśnięcie telefonem. Każdy z tych pomysłów rozwija inną część poznanej wiedzy.
Zachęta do modyfikowania gotowych rozwiązań uczy szukania własnych wariantów znanych mechanik. Wystarczy zmienić grafiki, cel gry albo parametry trudności, aby otrzymać nowy tytuł. Samodzielne projektowanie nawet małych gier to najlepszy trening przed autorskim projektem końcowym.
Komponent PhoneCall pozwala aplikacji wykonać połączenie, gdy najpierw ustawi się numer we właściwości PhoneNumber, a potem wywoła metodę MakePhoneCall. Podobnie działa komponent Texting, który wysyła wiadomość po ustawieniu numeru i treści właściwościami PhoneNumber oraz Message. Obie metody nie przyjmują argumentów, ponieważ dane pochodzą z właściwości komponentu.
Przed pierwszym użyciem system prosi użytkownika o zgodę, dlatego aplikacja powinna przewidzieć odmowę i poinformować o tym komunikatem. Zdarzenie MessageReceived umożliwia też odbieranie SMS-ów, co otwiera drogę do bardziej zaawansowanych funkcji. Przykładem łączącym komunikację z sensorami jest aplikacja alarmowa wysyłająca wiadomość z lokalizacją GPS.
Wykorzystanie telefonu i SMS ma realne skutki, takie jak koszty połączeń albo wysyłka niechcianych treści. Twórca powinien więc zapytać użytkownika o zgodę przed wysłaniem wiadomości i nie automatyzować jej bez potrzeby. Odpowiedzialne planowanie komunikacji jest częścią dobrego projektowania aplikacji mobilnych.
Komponent Sharing przekazuje treści między aplikacjami przez systemowe okno, w którym użytkownik wybiera program docelowy. Metody ShareMessage i ShareFile pozwalają wysłać odpowiednio sam tekst albo plik, a ShareFileWithMessage łączy oba rodzaje treści. Lista dostępnych programów zależy wyłącznie od tego, co jest zainstalowane na urządzeniu.
Udostępnianie nie wymaga żadnych uprawnień, ponieważ decyzję o odbiorcy zawsze podejmuje użytkownik. W grze "Łap piłkę" można w ten sposób podzielić się najlepszym wynikiem, a w aplikacji notatnikowej wysłać notatkę ze zdjęciem. Wywołanie metody Sharing to zwykle jeden blok w zdarzeniu przycisku.
Przed udostępnieniem pliku warto sprawdzić, czy on istnieje, bo ścieżka do zasobu mogła stać się nieaktualna. Komponent ułatwia też integrację z innymi programami bez pisania własnej obsługi sieci społecznościowych. Dzięki niemu aplikacja zyskuje naturalne wyjście treści do świata zewnętrznego.
Komponent Web wysyła do serwera zapytania HTTP, a odpowiedź odbiera w zdarzeniu GotText. Metody Get i PostText używają adresu zapisanego we właściwości Url, a wynik przychodzi z kodem oraz treścią odpowiedzi. W ten sposób aplikacja może pobierać dane z serwisów pogodowych, generatorów cytatów albo własnych serwerów.
Odpowiedzi z nowoczesnych usług mają zwykle format JSON, który w App Inventor rozbija się blokiem JsonTextDecode. Po rozkodowaniu dane stają się zwykłymi listami lub słownikami obsługiwanymi przez znane bloki. Znajomość tego przekształcenia otwiera drogę do łączenia aplikacji z publicznymi interfejsami API.
Komponent Web wymaga dostępu do internetu oraz poprawnego obsłużenia odpowiedzi, której treść bywa nieoczekiwana. Dlatego wartości zwracane przez serwer należy zawsze sprawdzać przed użyciem, na przykład warunkiem na kod odpowiedzi. Dzięki takim zabiegom aplikacja zachowuje się stabilnie nawet przy błędach po stronie usługi.
Komponent TinyWebDB przechowuje dane na zdalnym serwerze, dzięki czemu wiele urządzeń może odczytywać te same wartości. Metoda StoreValue zapisuje dane pod wskazanym tagiem, a GetValue rozpoczyna ich odczyt. W przeciwieństwie do TinyDB odczyt jest asynchroniczny, więc wynik należy obsłużyć w zdarzeniu GotValue.
Domyślną usługą jest testowy serwer MIT, na którym dane mogą zniknąć po pewnym czasie. Z tego powodu rankingi i komunikaty wymagają własnego serwera lub stałej usługi chmurowej. Komponent świetnie nadaje się do wspólnego rankingu graczy albo wymiany krótkich wiadomości między użytkownikami.
Opóźnienie sieciowe oznacza, że zapis i odczyt nie są natychmiastowe, dlatego aplikacja powinna informować o trwającej operacji. Wartość zwracana przez GotValue może być pusta, gdy tag jeszcze nie istnieje, co należy obsłużyć domyślną wartością. Uwzględnienie tych przypadków czyni aplikację niezawodną nawet przy wolnym połączeniu.
Aplikacje komunikacyjne łączą komponenty takie jak PhoneCall, Texting, Sharing i TinyWebDB w praktyczne narzędzia. Przycisk "Zadzwoń do mamy" dla seniora używa jednego wywołania PhoneCall i dużego, czytelnego elementu interfejsu. Aplikacja alarmowa wysyła SMS z bieżącymi współrzędnymi, gdy użytkownik naciśnie przycisk zagrożenia.
Udostępnianie zdjęć i notatek przez komponent Sharing sprawdza się w aplikacjach dziennikowych, a ranking w TinyWebDB motywuje do rywalizacji. Prosty komunikator grupowy może wymieniać krótkie wiadomości przez wspólną tabelę tagów. Wszystkie te przykłady łączą poznane wcześniej elementy z funkcją łączności.
Tworzenie aplikacji komunikacyjnych wymaga szczególnej uwagi poświęconej prywatności i kosztom. Wysyłka SMS, połączenia i przesyłanie danych zawsze wymagają zgody i świadomości użytkownika. Dobrze zaprojektowana aplikacja komunikacyjna informuje o każdej operacji, zanim ją wykona.
Projekt "Aktywny dzień" odpowiada na realny problem, jakim jest mało ruchu w ciągu dnia u osób korzystających z telefonów. Aplikacja ma liczyć kroki na podstawie odczytów akcelerometru i pokazywać postęp w przyjazny sposób. Dzięki temu każda porcja ruchu przekłada się na widoczny wynik, co wzmacnia motywację do aktywności.
Grupą docelową są uczniowie i studenci, dlatego interfejs powinien być prosty, kolorowy i oparty na dużych elementach. Grywalizacja, czyli cele, odznaki i komunikaty, zamienia codzienny wysiłek w rodzaj zabawy. Ważne jest, aby aplikacja działała w tle i nie wymagała ciągłego trzymania telefonu w ręku.
Projekt łączy większość tematów z prezentacji, od czujników po zapis danych i udostępnianie wyników. Dzięki temu stanowi sprawdzian wszystkich wcześniejszych umiejętności w jednej, spójnej aplikacji. Realizacja takiego pomysłu uczy też myślenia o potrzebach konkretnego użytkownika.
Specyfikacja porządkuje projekt i pozwala sprawdzić wykonanie każdej funkcji osobno. Wymienione funkcje F1-F6 opisują, co aplikacja ma robić, zanim napiszemy pierwszy blok. Taki zapis ułatwia też podział pracy na samodzielne, testowane etapy.
Liczenie kroków wymaga analizy odczytów akcelerometru, a wykres postępów - rysowania na Canvas i odczytu bazy TinyDB. Cele i komunikaty motywacyjne budują warstwę grywalizacji, a udostępnianie wyników domyka funkcje społeczne. Każda funkcja korzysta z innego komponentu, ale wszystkie współdzielą wspólne dane.
Dzięki czytelnym funkcjom projekt można badać po kolei, a pojedynczą usterkę łatwiej przypisać konkretnemu miejscu w kodzie. Gdy aplikacja łączy wiele komponentów, przewidywalna struktura staje się kluczowa dla odnalezienia błędu. Dlatego specyfikacja jest planem, do którego wraca się w trakcie całej budowy.
Aplikacja "Aktywny dzień" składa się z czterech ekranów, które dzielą zadania aplikacji na czytelne części. Ekran menu prowadzi do pozostałych widoków, licznik pokazuje bieżące kroki, a wykres przedstawia historię tygodnia. Ekran ustawień pozwala zmienić cel dzienny i wyzerować dane.
Wszystkie ekrany korzystają ze wspólnej bazy TinyDB, która przechowuje cel, licznik kroków i historię dni. Dzięki temu przejście między widokami nie gubi żadnych danych. Nawigację między ekranami buduje się najprościej za pomocą elementów otwierających kolejne Screeny.
Jeden spójny styl elementów i powtarzalne nazewnictwo ułatwiają poruszanie się po aplikacji. Zanim połączymy ekrany, warto przetestować każdy z nich osobno na prawdziwym telefonie. Taka struktura sprawia, że projekt pozostaje przejrzysty nawet przy rosnącej liczbie funkcji.
Liczenie kroków opiera się na analizie odczytów akcelerometru, który w trakcie chodzenia regularnie zmienia swoje wartości. Najprostszy algorytm wykrywa krok wtedy, gdy wartość przyspieszenia przekroczy ustawiony próg. Próg odcina drobne drgania telefonu, które nie są ruchem zgodnym z marszem.
Dodatkowy warunek czasowy sprawia, że kolejny krok zliczany jest dopiero po upływie odpowiedniego odstępu. Dzięki temu jedno szarpnięcie telefonem nie zostanie policzone jako wiele kroków. Wystarczy więc zmienna licznika, próg oraz zapamiętany czas ostatniego kroku.
Prawdziwe krokomierze używają bardziej złożonych algorytmów, między innymi filtrów i analizy kierunku ruchu. Nasza wersja jest uproszczona, ale dobrze pokazuje zasadę działania i sprawdza się na typowych telefonach. Dokładność można poprawić, testując różne progi na prawdziwym urządzeniu.
Wykres postępów rysujemy na obszarze Canvas, a dane do niego pobieramy z bazy TinyDB. Historia kroków jest przechowywana jako lista dni, którą kolejno odczytujemy i zamieniamy na słupki. Każdy słupek odpowiada jednemu dniowi, a jego wysokość zależy od liczby kroków.
Najprostszy słupek powstaje metodą DrawShape, która rysuje prostokąt na podstawie listy wierzchołków. Wysokość obliczamy, dzieląc liczbę kroków przez cel dzienny i mnożąc przez dostępną wysokość obszaru. Pod słupkami metodą DrawText umieszczamy oznaczenia kolejnych dni.
Skala wykresu powinna pasować do celu dnia, aby porównania pozostały czytelne. Przed rysowaniem warto wyczyścić Canvas metodą Clear, aby stare słupki nie nakładały się na nowe. Gotowy wykres daje użytkownikowi szybki podgląd, czy trzyma się planu.
Grywalizacja opiera się na mechanizmach znanych z gier, takich jak cele, punkty, odznaki i rankingi. Cele dzienne i tygodniowe ustalają progi, od których przyznajemy nagrodę, na przykład odznakę. Odznaki można przechowywać w TinyDB, aby pozostały widoczne po ponownym uruchomieniu.
Komunikaty motywacyjne pojawiające się po osiągnięciu połowy celu dodają aplikacji charakteru opiekuna. Komponent Notifier wyświetla tekst na ekranie, a TextToSpeech może go dodatkowo przeczytać na głos. Pasek postępu, którego nie ma w natywnych komponentach, rysujemy samodzielnie, na przykład na Canvas.
Udostępnianie wyników znajomym przez komponent Sharing wprowadza element współzawodnictwa. Grywalizacja jest sprawdzonym mechanizmem w aplikacjach fitness, ale należy stosować ją z umiarem. Zbyt duża liczba komunikatów może szybko zniechęcić użytkownika zamiast go motywować.
Plan wykonania dzieli budowę projektu na pięć tygodniowych etapów o określonych celach. W pierwszym tygodniu powstaje wygląd wszystkich ekranów, a w drugim zaczyna działać licznik kroków z zegarem. Taki podział pozwala w każdej chwili ocenić postęp prac i szybko reagować na opóźnienia.
Trzeci tydzień obejmuje zapis danych i wykres postępów, a czwarty dodaje grywalizację i udostępnianie wyników. Piąty tydzień przeznaczony jest na testy, poprawki i zbudowanie pliku .apk. Każdy etap kończy się działającym fragmentem aplikacji, co daje poczucie realnego postępu.
Ustalanie realistycznych celów zmniejsza ryzyko nieukończenia pracy w przewidzianym czasie. Jeśli jakaś funkcja okazuje się zbyt złożona, można uprościć jej zakres w kolejnym etapie. Planowanie to umiejętność przydatna w każdej pracy inżynierskiej, nie tylko przy tworzeniu aplikacji.
Czujniki dostarczają bardzo osobistych danych, takich jak położenie, ruch czy nagrania głosu, dlatego dostęp do nich wymaga zgody użytkownika. System operacyjny prosi o nią przy pierwszym użyciu komponentu, a aplikacja nie może obejść tej decyzji. Twórca powinien ponadto wyjaśnić, do czego służą żądane uprawnienia.
Zasada minimalizacji mówi, że aplikacja zbiera tylko te dane, których naprawdę potrzebuje. Nie należy wysyłać informacji osobowych do internetu bez wyraźnej wiedzy użytkownika, nawet jeśli czujniki je udostępniają. Lokalizacja i zdjęcia wymagają szczególnej ostrożności, bo dotyczą bezpośrednio osoby.
Przestrzeganie prywatności to również praktyka techniczna, na przykład nieprzechowywanie danych dłużej niż to konieczne. Zaufanie użytkownika buduje się przez przejrzystość i szacunek dla jego decyzji. Dobra aplikacja to nie tylko działający kod, lecz także odpowiedzialne traktowanie gromadzonych informacji.
Menu Build w App Inventor pozwala zbudować plik .apk albo pakiet .aab przygotowany pod Google Play. Wersja .apk instaluje się bezpośrednio na urządzeniu i wystarcza do testów oraz dystrybucji poza sklepem. Pakiet .aab jest natomiast wymagany przy publikowaniu w sklepie Google Play.
Publikacja w sklepie wiąże się z utworzeniem konta deweloperskiego i dostarczeniem opisu, ikony oraz zrzutów ekranu. Sklep oczekuje też polityki prywatności, zwłaszcza gdy aplikacja korzysta z lokalizacji albo aparatu. Po publikacji kolejne poprawki trafiają do użytkowników jako aktualizacje.
Alternatywą dla sklepu jest rozesłanie pliku .apk znajomym, na przykład przez link lub kod QR. Taka dystrybucja jest szybsza, ale użytkownik musi sam zezwolić na instalację z nieznanego źródła. Wybór sposobu publikacji zależy od odbiorców aplikacji i celu jej powstania.
Cykl powstawania aplikacji kończy się wprowadzeniem, jednak prawdziwa praca programisty zaczyna się dopiero po jej udostępnieniu. Wersja przekazana użytkownikom zwykle wymaga ciągłego doskonalenia, a kolejne aktualizacje opierają się na zgłoszeniach i opiniach płynących z praktyki. Umiejętność zamknięcia cyklu, a następnie rozpoczęcia kolejnego, należy do kluczowych kompetencji zespołów tworzących oprogramowanie.
Poszczególne etapy opisane na tym slajdzie warto porównać ze sposobem pracy, jaki stosowaliśmy podczas budowy gry i przygotowywania wszystkich wcześniejszych prezentacji. Nawet w uproszczonych projektach można odnaleźć fazy planowania, realizacji i testów, co pokazuje uniwersalność tego podejścia. Dostrzeżenie tej analogii ułatwia przenoszenie dobrych nawyków na większe przedsięwzięcia.
Na zakończenie całego kursu warto zapamiętać, że każda z pięciu prezentacji ilustruje kolejny stopień tej samej drabiny umiejętności. Wspólnie składają się one na pełny obraz pracy nad aplikacją mobilną od pierwszego pomysłu aż po wdrożenie. Świadomość przebiegu całego procesu to wartość, która pozostanie z kursantami niezależnie od narzędzi, z jakich będą korzystać w przyszłości.
Wymienione pytania obejmują najważniejsze umiejętności z całej prezentacji, dlatego każda poprawna odpowiedź stanowi dobrą, wiarygodną prognozę wyniku egzaminu. Do pytania o akcelerometr warto wrócić, ćwicząc na działającym przykładzie poziomicy albo sterowania przechyłem. Taka powtórka w praktyce utrwala wiedzę znacznie skuteczniej niż samo czytanie odpowiedzi.
Pytania o przechowywanie danych w czasie działania aplikacji, a zwłaszcza o różnicę między TinyDB a TinyWebDB, dobrze ilustrują architekturę typowych aplikacji mobilnych. Lokalna baza odpowiada za szybki dostęp do własnych danych, natomiast serwerowa umożliwia wymianę między urządzeniami. Umiejętność wyboru właściwego rozwiązania zależy od scenariusza działającej aplikacji.
Ostatnie zadanie, polegające na zaprojektowaniu własnej aplikacji z uzasadnieniem wyboru czujnika, wiąże się ściśle z projektem autorskim. Formułowanie uzasadnienia, czyli wskazanie problemu użytkownika rozwiązywanego przez dany sensor, uczy myśleć o technologii jako o narzędziu. Warto przećwiczyć takie uzasadnienie na głos, ponieważ to umiejętność przydająca się również podczas prezentacji projektu.
Materiały pomocnicze zostały tak dobrane, aby każdy student mógł samodzielnie pogłębić poznane zagadnienia we własnym tempie. Kompendia pam1-pam4 stanowią przewodnik po kolejnych częściach kursu i należy je czytać razem z prezentacją, a nie zamiast niej. Ułożenie materiałów w kolejności ułatwia systematyczne budowanie wiedzy od podstaw po zaawansowane mechanizmy.
Serwisy internetowe wskazane na slajdzie prowadzą do oficjalnej dokumentacji i do społeczności skupionej wokół platformy. Warto regularnie zaglądać do opisu komponentów, gdyż właściwości i zdarzenia bywają aktualizowane wraz z kolejnymi wersjami usługi. Dyskusje na forach często zawierają praktyczne przykłady rozwiązań, których nie ma w podstawowej dokumentacji.
Uważne czytanie źródłowych materiałów uczy też sprawdzania poprawności informacji, co stanowi ważną umiejętność w każdej branży technicznej. Zachęta zawarta w notacie dotyczącej aktualności adresów przypomina, że świat technologii zmienia się bardzo szybko. Wiedza o tym, gdzie szukać aktualnych danych, pozwala wykorzystać umiejętności zdobyte podczas całego kursu również po jego zakończeniu.
Powodzenia w tworzeniu własnych aplikacji mobilnych!
Przebyta droga przez kolejne prezentacje ułożyła się w logiczny ciąg nauki, którego ostatnim ogniwem były czujniki, multimedia i gry. Warto teraz spojrzeć wstecz i zauważyć, ile pojedynczych umiejętności przyczyniło się do powstania działających aplikacji. Świadomość tego postępu jest mocnym bodźcem do dalszego rozwijania wiedzy programistycznej.
Blokowe środowiska, takie jak App Inventor, w połączeniu z nauką projektowania przygotowują grunt pod przejście do języków tekstowych. Konstrukcje warunkowe, pętle i zdarzenia mają dokładne odpowiedniki w powszechnie używanych technologiach, dlatego zdobyta wiedza nie przepada. Każde kolejne samodzielne zadanie, nawet najprostsze, przybliża do swobodnego tworzenia oprogramowania.
Ukończenie kursu to dopiero początek, a najlepszym sposobem dalszego rozwoju jest realizacja własnych pomysłów i dzielenie się nimi z innymi. Zapraszamy do pozostawania w kontakcie ze społecznością uczelni oraz do eksperymentowania na własną rękę. Życzymy powodzenia w tworzeniu aplikacji i w całej dalszej drodze zawodowej.