Baza książek - slajd 1/45
Baza przeczytanych książek - strona tytułowa
Cel slajdu: zapowiedź ćwiczenia laboratoryjnego: budowa mobilnej bazy danych książek z lokalnym magazynem TinyDB, skanerem kodów kreskowych i sieciowym API.

Strona tytułowa ćwiczenia

  • Tytuł projektu: Domowa biblioteczka i baza przeczytanych książek w środowisku MIT App Inventor.
  • Zakres funkcjonalny: rejestracja metadanych (tytuł, autor, wydawnictwo, rok, gatunek, status, ocena, recenzja), trwały zapis w pamięci flash (TinyDB), skanowanie kodów kreskowych aparatem (BarcodeScanner), asynchroniczne pobieranie danych z Open Library API (komponent Web, dekodowanie JSON do słowników), filtrowanie listy, statystyki czytelnika oraz eksport do pliku CSV.
  • Forma dydaktyczna: laboratoryjny przewodnik krok po kroku: od koncepcji architektury, przez projektowanie interfejsu (Designer), kodowanie logiki blokowej (Blocks), aż po weryfikację na smartfonie i kompilację pakietu APK.
  • Materiały bazowe: moduły wykładowe pam1.pdf-pam5.pdf oraz oficjalna dokumentacja MIT App Inventor.
  • Metodyka: każda faza tworzy w pełni przetestowany, działający przyrost aplikacji mobilnej.
Baza książek - slajd 2/45
Spis treści ćwiczenia
Cel slajdu: przedstawienie struktury ćwiczenia oraz planu pracy laboratoryjnej w ośmiu logicznych etapach.

Plan pracy nad bazą książek

Baza książek - slajd 3/45
Cele dydaktyczne ćwiczenia
Cel slajdu: zdefiniowanie efektów uczenia się w kategoriach wiedzy, umiejętności i kompetencji inżynierskich.

Czego nauczysz się podczas tych zajęć

  • Wiedza teoretyczna:
    • Poznasz zasadę działania pamięci nieulotnej w systemie Android na przykładzie komponentu TinyDB.
    • Zrozumiesz architekturę zapytań sieciowych REST API, strukturę formatu JSON oraz kody odpowiedzi protokołu HTTP (200, 404).
    • Opanujesz różnice pomiędzy strukturami listowymi a słownikami asocjacyjnymi (szuflada Dictionaries).
  • Umiejętności praktyczne:
    • Zaprojektujesz ergonomiczny, przewijany formularz mobilny z kontrolkami wyboru (Spinner) i listą (ListView).
    • Zaimplementujesz pełen zestaw operacji CRUD (tworzenie, odczyt, modyfikacja, usuwanie rekordów).
    • Zintegrujesz kamerę telefonu jako skaner kodów kreskowych EAN-13/ISBN za pomocą BarcodeScanner.
    • Zaprogramujesz asynchroniczne pobieranie danych przez Web i automatyczne parsowanie odpowiedzi JSON.
    • Zaimplementujesz algorytm analityki czytelniczej oraz eksport bazy do standardu CSV.
  • Kompetencje inżynierskie: umiejętność tworzenia oprogramowania odpornego na brak łączności sieciowej i błędne dane wejściowe użytkownika.
Baza książek - slajd 4/45
Koncepcja i model działania aplikacji
Cel slajdu: wyjaśnienie idei cyfrowego rejestru bibliotecznego oraz schematu obiegu informacji w systemie mobilnym.

Cyfrowy asystent domowej biblioteczki

  • Problem użytkownika: tradycyjny miłośnik literatury gromadzi setki tomów, zapominając, co już przeczytał, które pozycje ocenił najwyżej oraz komu pożyczył wybrane egzemplarze.
  • Rozwiązanie inżynierskie: autonomiczna aplikacja na smartfon, która łączy lokalną bazę danych z kamerą i globalnym katalogiem bibliotecznym.
  • Schemat obiegu informacji w systemie:
    1. Skanowanie: Użytkownik zbliża obiektyw do kodu paskowego na okładce; komponent BarcodeScanner odczytuje numer ISBN.
    2. Pobieranie sieciowe: Komponent Web wysyła zapytanie HTTP GET do otwartego API Open Library.
    3. Ekstrakcja danych: Otrzymany tekst JSON zostaje zdekodowany do słownika, a pola tytułu, autora i roku wypełniają się same.
    4. Wzbogacenie wpisu: Czytelnik wybiera gatunek ze Spinnera, zaznacza status, przyznaje gwiazdki i dopisuje notatkę.
    5. Trwały zapis: Rekord zostaje zserializowany i zapisany w TinyDB, a widok ListView natychmiast się aktualizuje.
Pamiętaj: Aplikacja zachowuje pełną funkcjonalność offline! Jeśli zabraknie Internetu, wszystkie dane można wpisać i zapisać ręcznie.
Baza książek - slajd 5/45
Specyfikacja wymagań funkcjonalnych
Cel slajdu: sformalizowanie listy funkcji aplikacji oraz kryteriów ich akceptacji przed przystąpieniem do prac w Designerze.

Co dokładnie ma robić nasza aplikacja

  • Moduł ewidencji (CRUD):
    • Wprowadzanie nowej książki do bazy z poziomu czytelnego formularza.
    • Przeglądanie listy zarejestrowanych pozycji w komponencie ListView.
    • Edycja istniejącego wpisu po kliknięciu wybranego elementu na liście.
    • Bezpieczne usuwanie książki z bazy z wymaganym dialogiem potwierdzenia (Notifier).
  • Pola rekordu książki: numer ISBN, Tytuł (pole obowiązkowe), Autor, Wydawnictwo, Rok wydania, Liczba stron, Gatunek (lista wyboru), Status czytania, Ocena (1-5 gwiazdek), Krótka notatka/recenzja oraz Data rejestracji.
  • Moduł sprzętowy: skanowanie kodu paskowego EAN-13/ISBN za pomocą wbudowanego skanera kamery.
  • Moduł integracji sieciowej: asynchroniczne zapytanie HTTP GET do serwera Open Library i automatyczne uzupełnienie formularza danymi z formatu JSON.
  • Moduł analityczny i społecznościowy: dynamiczne filtrowanie listy, licznik tomów, kalkulator średniej ocen, udostępnianie recenzji (Sharing) oraz eksport do CSV.
Specyfikacja to kontrakt inżynierski. Jasno zdefiniowane pola determinują strukturę danych w blokach i wygląd interfejsu w Designerze.
Baza książek - slajd 6/45
Struktury danych: słowniki i listy w App Inventor
Cel slajdu: zrozumienie struktur danych: dlaczego pojedyncza książka to słownik (Dictionary), a cała baza to lista słowników.

Organizacja danych w pamięci programu

  • Problem tradycyjnych list w AI2: Dawniej rekordy tworzono jako listę list (np. indeks 1 = ISBN, indeks 2 = Tytuł). Zmiana kolejności pól lub dodanie nowego atrybutu niszczyło całą logikę programu!
  • Nowoczesne rozwiązanie: Słowniki (szuflada Dictionaries):
    • Słownik przechowuje dane w parach klucz -> wartość.
    • Dostęp do danych uzyskujemy przez czytelną nazwę klucza, np. get value for key "tytul".
    • Struktura jest elastyczna: dodanie klucza "strony" nie psuje istniejącego kodu.
  • Model rekordu książki (pojedynczy słownik):
    {
      "isbn": "9788308064559",
      "tytul": "Katedra",
      "autor": "Jacek Dukaj",
      "wydawnictwo": "Wydawnictwo Literackie",
      "rok": "2018",
      "strony": "103",
      "gatunek": "Fantastyka i Sci-Fi",
      "status": "Przeczytana",
      "ocena": "★★★★★ (5/5)",
      "notatka": "Genialne opowiadanie metafizyczne.",
      "data_dodania": "2026-09-23"
    }
  • Model całej bazy: zmienna globalna bazaKsiazek to lista zawierająca wiele takich słowników: [ {książka 1}, {książka 2}, ... ].
Baza książek - slajd 7/45
Ergonomia i interfejs mobilny
Cel slajdu: zasady projektowania intuicyjnego interfejsu mobilnego łączącego formularz wprowadzania z przeglądarką rekordów.

Architektura jednego przewijanego ekranu

  • Wyzwanie projektowe: Formularz zawiera 9 kontrolek, a pod nim musimy umieścić przyciski akcji, statystyki oraz pełną listę książek z wyszukiwarką. Jak uniknąć ciasnoty?
  • Zasada pionowej hierarchii:
    • Ekran nadrzędny Screen1 otrzymuje właściwość Scrollable = True.
    • U góry umieszczamy sekcję identyfikacji (kod ISBN, skaner, pobieranie z sieci).
    • Poniżej znajduje się zwarty formularz z podpowiedziami (właściwość Hint zamiast zbędnych etykiet).
    • Środek ekranu zajmuje wyrazisty pasek przycisków akcji o zróżnicowanych barwach semantycznych.
    • Na dole umieszczamy komponent ListView z paskiem szybkiego filtrowania oraz podsumowanie statystyczne.
  • Semantyka kolorystyczna przycisków:
    • Zapisz / Aktualizuj: zieleń szmaragdowa (#27AE60) - akcja zatwierdzenia.
    • Wyczyść formularz: neutralna szarość (#7F8C8D) - akcja resetu.
    • Usuń pozycję: czerwień bordowa (#C0392B) - operacja destrukcyjna.
    • Skanuj / Sieć: granat i błękit (#2C3E50 / #2980B9) - operacje technologiczne.
Baza książek - slajd 8/45
Osiem etapów pracy - przegląd metodyczny
Cel slajdu: przedstawienie dyscypliny metodycznej i sekwencji realizacyjnej projektu.

Kolejność budowy aplikacji laboratoryjnej

EtapZakres działańOczekiwany rezultat
Etap 1Nowy projekt i konfiguracja Screen1Skonfigurowany ekran nadrzędny i kontenery sekcji.
Etap 2Układ kontrolek w oknie DesignerKompletny interfejs: formularz, przyciski, lista i sensory.
Etap 3Zmienne stanu i odczyt z TinyDBStabilna inicjalizacja bazy w zdarzeniu Initialize.
Etap 4Implementacja pełnego cyklu CRUDDodawanie, walidacja, wybór, edycja i usuwanie wpisów.
Etap 5Skaner kodów i zapytania do APIOptyczny odczyt ISBN oraz pobieranie JSON z Open Library.
Etap 6Narzędzia analityczne i eksportWyszukiwarka, licznik lektur, udostępnianie i CSV.
Etap 7Testy na telefonie i diagnostykaPrzetestowanie aplikacji w MIT AI2 Companion.
Etap 8Kompilacja i generowanie pliku APKGotowa aplikacja zainstalowana w systemie Android.
Zasada laboratoryjna: Nie przechodź do kolejnego etapu, dopóki bieżący krok nie działa w 100% poprawnie na Twoim urządzeniu!
Baza książek - slajd 9/45
Etap 1: Nowy projekt i konfiguracja Screen1
Cel slajdu: rozpoczęcie nowego projektu w AI2 i skonfigurowanie kluczowych parametrów ekranu głównego.

Krok 1: Tworzenie projektu i parametry Screen1

  • Zaloguj się do serwisu http://ai2.appinventor.mit.edu.
  • Kliknij menu Projects -> Start new project.
  • Wpisz nazwę projektu: BazaKsiazek (uwaga: nazwa nie może zawierać spacji ani polskich znaków diakrytycznych!).
  • Przejdź do panelu Properties dla komponentu Screen1 i ustaw właściwości:
    • AppName: Moja Biblioteczka (nazwa widoczna pod ikoną na pulpicie telefonu).
    • Title: Moja Biblioteczka - Baza (tekst widoczny na górnej belce aplikacji).
    • Scrollable: ZAZNACZ (True) - kluczowe dla formularza z klawiaturą!
    • AlignHorizontal: Center (wyśrodkowanie kontenerów podrzędnych).
    • BackgroundColor: #F4F6F9 (jasnoszary, elegancki odcień tła).
    • ScreenOrientation: Portrait (blokada orientacji pionowej smartfona).
Blokada orientacji Portrait zapobiega przeładowywaniu ekranu przy przypadkowym przechyleniu telefonu podczas skanowania książki!
Baza książek - slajd 10/45
Etap 1: Architektura układu ekranu i kontenery nadrzędne
Cel slajdu: zbudowanie stabilnego szkieletu układu z wykorzystaniem komponentów Layout.

Krok 2: Podział ekranu na sekcje nadrzędne

  • Z palety Layout przeciągnij na ekran główny kontener pionowy: VerticalArrangement.
  • Zmień jego nazwę w panelu Components na vaGlowny (przycisk Rename).
  • Ustaw właściwości vaGlowny:
    • Width: Fill parent (rozciągnięcie na pełną szerokość ekranu).
    • Height: Automatic.
    • AlignHorizontal: Center.
  • Wewnątrz vaGlowny utwórz trzy logiczne strefy interfejsu:
    1. vaSekcjaFormularz (VerticalArrangement): kontener na pola wprowadzania danych (Width: Fill parent, BackgroundColor: White).
    2. haSekcjaPrzyciski (HorizontalArrangement): kontener na przyciski operacyjne CRUD (Width: Fill parent, AlignHorizontal: Center).
    3. vaSekcjaBaza (VerticalArrangement): kontener na listę książek i narzędzia analityczne (Width: Fill parent).
Dobra praktyka: Zastosowanie białego tła w kontenerze formularza na szarym tle ekranu tworzy nowoczesny efekt wizualnej "karty" (ang. card layout).
Baza książek - slajd 11/45
Etap 2: Sekcja kodu ISBN - pole tekstowe i przyciski akcji
Cel slajdu: zaprojektowanie pierwszego rzędu formularza: obsługa numeru ISBN, skanera aparatem i wyszukiwarki sieciowej.

Krok 3: Identyfikator ISBN i integracja z aparatem

  • Do kontenera vaSekcjaFormularz dodaj HorizontalArrangement i nazwij go haISBN.
  • Ustaw właściwości haISBN: Width = Fill parent, AlignVertical = Center.
  • Wewnątrz haISBN umieść trzy komponenty obok siebie:
    1. txtISBN (TextBox z palety User Interface):
      • Width: Fill parent (zajmuje całą wolną szerokość z lewej strony).
      • Hint: Wpisz lub zeskanuj ISBN...
      • FontSize: 14, NumbersOnly: False (zostawiamy False, aby obsłużyć starsze numery ISBN z literą X).
    2. btnSkanuj (Button):
      • Text: 📷 Skanuj, BackgroundColor: #2C3E50, TextColor: White.
    3. btnSzukajSiec (Button):
      • Text: 🌐 Pobierz, BackgroundColor: #2980B9, TextColor: White.
Przycisk Skanuj uruchomi kamerę, a przycisk Pobierz wyśle zapytanie do serwerów Open Library w celu pobrania tytułu i autora!
Baza książek - slajd 12/45
Etap 2: Pola metadanych - tytuł, autor, wydawnictwo i rok
Cel slajdu: dodanie i skonfigurowanie podstawowych pól tekstowych formularza ewidencyjnego.

Krok 4: Główne pola tekstowe książki

  • Wewnątrz kontenera vaSekcjaFormularz (pod rzędem ISBN) dodaj kolejne kontrolki:
    1. txtTytul (TextBox):
      • Width: Fill parent, Hint: Tytuł książki (pole wymagane).
      • FontSize: 15, FontBold: True.
    2. txtAutor (TextBox):
      • Width: Fill parent, Hint: Autor (Imię i Nazwisko).
      • FontSize: 14.
    3. txtWydawnictwo (TextBox):
      • Width: Fill parent, Hint: Wydawnictwo, FontSize: 14.
    4. Dodaj kontener poziomy haRokStrony (Width: Fill parent):
      • txtRok (TextBox wewnątrz haRokStrony): Width = 48%, Hint = Rok wydania, NumbersOnly = True.
      • txtStrony (TextBox wewnątrz haRokStrony): Width = 48%, Hint = Liczba stron, NumbersOnly = True.
Uwaga ergonomiczna: Dla roku i liczby stron ustawiliśmy NumbersOnly = True. Dzięki temu telefon wyświetli od razu klawiaturę numeryczną!
Baza książek - slajd 13/45
Etap 2: Kategoryzacja książki - komponenty Spinner
Cel slajdu: zastosowanie list rozwijanych (Spinner) do wyboru gatunku, statusu czytania i oceny książki.

Krok 5: Kontrolki wyboru (Gatunek, Status, Ocena)

  • Do kontenera vaSekcjaFormularz dodaj trzy komponenty Spinner (paleta User Interface):
    1. spnGatunek (Gatunek literacki):
      • Width: Fill parent, Prompt: Wybierz gatunek literacki.
      • ElementsFromString: Literatura piękna, Kryminał i thriller, Fantastyka i Sci-Fi, Reportaż i biografia, Popularnonaukowa, Historia, Psychologia i rozwój, Poezja, Inny.
    2. spnStatus (Status lektury):
      • Width: Fill parent, Prompt: Wybierz status książki.
      • ElementsFromString: Przeczytana, W trakcie czytania, Do przeczytania, Porzucona.
    3. spnOcena (Ocena w skali 1-5):
      • Width: Fill parent, Prompt: Wystaw ocenę książce.
      • ElementsFromString: ★★★★★ (5/5) Wybitna, ★★★★☆ (4/5) Bardzo dobra, ★★★☆☆ (3/5) Dobra, ★★☆☆☆ (2/5) Przeciętna, ★☆☆☆☆ (1/5) Słaba, Bez oceny.
Dlaczego Spinner? Zastosowanie list rozwijanych gwarantuje spójność danych. Żaden użytkownik nie zrobi literówki w nazwie gatunku!
Baza książek - slajd 14/45
Etap 2: Pole notatki i przyciski operacyjne formularza (CRUD)
Cel slajdu: zaimplementowanie wielowierszowego pola recenzji oraz paska przycisków zarządzania rekordem.

Krok 6: Recenzja i pasek przycisków CRUD

  • W kontenerze vaSekcjaFormularz dodaj wielowierszowe pole tekstowe:
    • txtNotatka (TextBox): Width = Fill parent, Height = 70px.
    • MultiLine: True, Hint: Własna recenzja, cytat, przemyślenia...
  • W kontenerze haSekcjaPrzyciski (pod formularzem) umieść trzy przyciski:
    1. btnZapisz (Button):
      • Text: 💾 Zapisz książkę, BackgroundColor: #27AE60 (zielony), TextColor: White, FontBold: True.
    2. btnNowa (Button):
      • Text: 📄 Wyczyść, BackgroundColor: #7F8C8D (szary), TextColor: White.
    3. btnUsun (Button):
      • Text: 🗑️ Usuń, BackgroundColor: #C0392B (czerwony), TextColor: White.
      • Enabled: False (nieaktywny do momentu wybrania książki z bazy!).
Baza książek - slajd 15/45
Etap 2: Sekcja przeglądania bazy - komponent ListView z filtrem
Cel slajdu: skonfigurowanie komponentu ListView jako dynamicznego widoku zarejestrowanych książek.

Krok 7: Przeglądarka księgozbioru (ListView)

  • Wewnątrz kontenera vaSekcjaBaza dodaj:
    1. lblTytulBazy (Label): Text = 📚 Zarejestrowane książki w bazie:, FontSize = 15, FontBold = True, TextColor = #2C3E50.
    2. lvKsiazki (komponent ListView z palety User Interface):
      • Width: Fill parent.
      • Height: 260px (stała wysokość zapobiega konfliktom przewijania z ekranem Screen1!).
      • ShowFilterBar: True (włącza natywną wyszukiwarkę na szczycie listy!).
      • BackgroundColor: White (czyste, kontrastowe tło).
      • TextColor: #2C3E50 (ciemnoszary, czytelny tekst).
      • SelectionColor: #D5F5E3 (seledynowe podświetlenie wybranej pozycji).
      • TextSize: 14.
Właściwość ShowFilterBar = True to potężne narzędzie: tworzy dynamiczne pole wyszukiwania, które na żywo zawęża listę w trakcie pisania!
Baza książek - slajd 16/45
Etap 2: Narzędzia rozszerzone - statystyki, polecanie i eksport CSV
Cel slajdu: rozbudowa interfejsu o panel analityki czytelniczej i funkcje eksportu danych.

Krok 8: Panel analityki i funkcje społecznościowe

  • Poniżej komponentu lvKsiazki w kontenerze vaSekcjaBaza dodaj:
    1. lblStatystyki (Label):
      • Width: Fill parent, FontSize: 13, FontBold: True.
      • BackgroundColor: #EAEDED, TextColor: #34495E.
      • Text: Łącznie książek: 0 | Przeczytane: 0 | Średnia ocena: -
    2. Dodaj kontener poziomy haNarzedzia (Width = Fill parent, AlignHorizontal = Center):
      • btnUdostepnij (Button): Text = 📢 Poleć książkę, BackgroundColor = #3498DB, TextColor = White.
      • btnEksportCSV (Button): Text = 📊 Eksport CSV, BackgroundColor = #16A085, TextColor = White.
Wartość dydaktyczna: Statystyki i eksport pokazują studentom, jak zamienić zwykły notatnik w pełnowartościowy system informacji zarządczej.
Baza książek - slajd 17/45
Etap 2: Komponenty niewidoczne i audyt drzewa komponentów
Cel slajdu: dodanie komponentów funkcjonalnych do strefy Non-visible oraz weryfikacja nazewnictwa.

Krok 9: Komponenty niewidoczne (Non-visible components)

  • Z odpowiednich palet przeciągnij na ekran roboczy komponenty niewidoczne:
    1. TinyDB1 (paleta Storage): lokalna baza danych w pamięci trwałej smartfona.
    2. BarcodeScanner1 (paleta Sensors):
      • Właściwość UseExternalScanner: ODZNACZ (False)! Jest to warunek konieczny, aby skaner działał wewnątrz naszej aplikacji.
    3. Web1 (paleta Connectivity): moduł asynchronicznej komunikacji sieciowej HTTP.
    4. Notifier1 (paleta User Interface): dymki powiadomień Toast i okna dialogowe potwierdzeń.
    5. Clock1 (paleta Sensors): zegar systemowy do rejestracji daty dodania książki.
    6. Sharing1 (paleta Social): natywny mechanizm udostępniania treści w systemie Android.
  • Kontrola w panelu Components: Sprawdź, czy każdy komponent ma poprawną, polską nazwę semantyczną z prefiksem!
Baza książek - slajd 18/45
Etap 3: Zmienne globalne stanu aplikacji
Cel slajdu: zdefiniowanie i zainicjalizowanie zmiennych przechowujących dane w pamięci operacyjnej programu.

Krok 10: Deklaracja stanu w oknie Blocks

  • Przełącz się do widoku Blocks (przycisk w prawym górnym rogu ekranu).
  • Z szuflady Variables wyciągnij bloki i zadeklaruj trzy zmienne globalne:
    1. global bazaKsiazek:
      • Inicjalizacja: blok create empty list (szuflada Lists).
      • Rola: główna struktura danych przechowująca całą bazę książek (listę słowników) w pamięci RAM.
    2. global indeksEdycji:
      • Inicjalizacja: blok liczbowy 0 (szuflada Math).
      • Rola: przechowuje numer indeksu aktualnie edytowanej książki. Wartość 0 oznacza tryb dodawania nowej pozycji, a wartość > 0 oznacza edycję istniejącego tomu.
    3. global NAZWA_TAGU:
      • Inicjalizacja: blok tekstowy "MojaBazaKsiazek" (szuflada Text).
      • Rola: stała tekstowa reprezentująca klucz w bazie TinyDB.
Baza książek - slajd 19/45
Etap 3: Zdarzenie Screen1.Initialize i bezpieczny odczyt z TinyDB
Cel slajdu: prawidłowe załadowanie bazy danych podczas startu aplikacji z obsługą pierwszego uruchomienia.

Krok 11: Bezpieczny start programu (Initialize)

  • Z szuflady Screen1 wybierz blok zdarzenia: when Screen1.Initialize do:
    1. Odczytaj bazę z pamięci flash za pomocą bloku TinyDB:
      set global bazaKsiazek to call TinyDB1.GetValue(
          tag = get global NAZWA_TAGU,
          valueIfTagNotThere = create empty list
      )
    2. Sprawdź integralność typu: upewnij się, czy zwrócona wartość jest listą (blok is a list?). Jeśli nie (np. po błędzie starej wersji), przypisz create empty list.
    3. Wywołaj procedurę odświeżenia widoku: call odswiezListView.
    4. Wywołaj procedurę obliczenia statystyk: call przeliczStatystyki.
    5. Wywołaj procedurę czyszczenia formularza: call wyczyscFormularz.
Gniazdo valueIfTagNotThere chroni program przed awarią przy pierwszym uruchomieniu po instalacji, gdy w bazie TinyDB nie ma jeszcze żadnych danych!
Baza książek - slajd 20/45
Etap 3: Procedura odświeżania listy na ekranie (odswiezListView)
Cel slajdu: zbudowanie procedury transformującej listę słowników na czytelne wpisy komponentu ListView.

Krok 12: Transformacja danych do widoku ListView

  • Z szuflady Procedures wyciągnij blok: to procedure odswiezListView:
    1. Utwórz zmienną lokalną: listaWierszy = create empty list.
    2. Pętla po wszystkich rekordach: for each ksiazka in list (get global bazaKsiazek):
      • Wyciągnij wartości ze słownika ksiazka:
        tytul = get value for key "tytul" in dictionary (ksiazka)
        autor = get value for key "autor" in dictionary (ksiazka)
        ocena = get value for key "ocena" in dictionary (ksiazka)
        status = get value for key "status" in dictionary (ksiazka)
      • Zbuduj czytelną pozycję tekstową blokiem join:
        napis = join(ocena, " | „", tytul, "” — ", autor, " [", status, "]")
      • Dodaj utworzony wiersz: add items to list (list = listaWierszy, item = napis).
    3. Przypisz gotową listę do komponentu:
      set lvKsiazki.Elements to get local listaWierszy
Baza książek - slajd 21/45
Etap 4: Walidacja danych formularza przed zapisem
Cel slajdu: zabezpieczenie bazy przed wprowadzaniem pustych lub niespójnych danych.

Krok 13: Ochrona integralności bazy danych

  • Zasada inżynierska: Błędne dane na wejściu to zepsuta baza na wyjściu (Garbage In, Garbage Out).
  • Przed zapisem książki musimy zweryfikować poprawność pól:
    1. Test 1 - Wymagany tytuł:
      if is empty (trim(txtTytul.Text)) then
          call Notifier1.ShowAlert("Błąd: Tytuł książki nie może być pusty!")
          // przerwij dalsze wykonywanie zdarzenia
    2. Test 2 - Poprawność roku:
      if not (is empty(txtRok.Text)) and not (is a number?(txtRok.Text)) then
          call Notifier1.ShowAlert("Błąd: Rok wydania musi być liczbą!")
          // przerwij dalsze wykonywanie zdarzenia
    3. Test 3 - Logiczny przedział roku: Sprawdź, czy rok mieści się w zakresie od roku 1000 do roku bieżącego (np. 2026).
Blok trim z szuflady Text usuwa spacje z początku i końca tekstu. Zapobiega to oszukaniu walidacji poprzez wpisanie samej spacji!
Baza książek - slajd 22/45
Etap 4: Tworzenie rekordu słownikowego (make a dictionary)
Cel slajdu: skonstruowanie struktury danych reprezentującej pojedynczą książkę za pomocą bloków Dictionaries.

Krok 14: Budowa słownika książki z kontrolek

  • Z szuflady Dictionaries wyciągnij blok make a dictionary.
  • Kliknij niebieskie koło zębate (mutator) i dodaj 11 par klucz-wartość (pair):
    1. para: klucz = "isbn", wartość = txtISBN.Text
    2. para: klucz = "tytul", wartość = txtTytul.Text
    3. para: klucz = "autor", wartość = txtAutor.Text
    4. para: klucz = "wydawnictwo", wartość = txtWydawnictwo.Text
    5. para: klucz = "rok", wartość = txtRok.Text
    6. para: klucz = "strony", wartość = txtStrony.Text
    7. para: klucz = "gatunek", wartość = spnGatunek.Selection
    8. para: klucz = "status", wartość = spnStatus.Selection
    9. para: klucz = "ocena", wartość = spnOcena.Selection
    10. para: klucz = "notatka", wartość = txtNotatka.Text
    11. para: klucz = "data_dodania", wartość = call Clock1.FormatDate(Clock1.Now(), "yyyy-MM-dd")
Zapisujemy ten słownik w zmiennej lokalnej nowyRekord wewnątrz zdarzenia kliknięcia przycisku Zapisz!
Baza książek - slajd 23/45
Etap 4: Zapis nowej pozycji i aktualizacja bazy TinyDB
Cel slajdu: zaimplementowanie zdarzenia kliknięcia btnZapisz dla nowej pozycji z trwałym utrwaleniem w pamięci flash.

Krok 15: Logika zdarzenia btnZapisz.Click

  • Z szuflady btnZapisz wybierz blok: when btnZapisz.Click do:
    1. Wykonaj walidację (sprawdź, czy tytuł nie jest pusty).
    2. Zbuduj słownik i przypisz do zmiennej lokalnej nowyRekord.
    3. Sprawdź tryb operacji:
      if get global indeksEdycji = 0 then
          // TRYB DODAWANIA NOWEJ KSIĄŻKI:
          add items to list (list = get global bazaKsiazek, item = get local nowyRekord)
          call Notifier1.ShowAlert("Pomyślnie dodano książkę do bazy!")
      else
          // TRYB EDYCJI ISTNIEJĄCEJ KSIĄŻKI (omówimy na slajdzie 26)
    4. Zsynchronizuj pamięć trwałą telefonu:
      call TinyDB1.StoreValue(tag = get global NAZWA_TAGU, valueToStore = get global bazaKsiazek)
    5. Zaktualizuj interfejs:
      call odswiezListView
      call przeliczStatystyki
      call wyczyscFormularz
Baza książek - slajd 24/45
Etap 4: Czyszczenie formularza i resetowanie stanu edycji
Cel slajdu: napisanie procedury przywracającej formularz do czystego stanu początkowego.

Krok 16: Procedura wyczyscFormularz i zdarzenie btnNowa

  • Zbuduj procedurę: to procedure wyczyscFormularz:
    1. Wyczyść pola tekstowe:
      set txtISBN.Text to ""
      set txtTytul.Text to ""
      set txtAutor.Text to ""
      set txtWydawnictwo.Text to ""
      set txtRok.Text to ""
      set txtStrony.Text to ""
      set txtNotatka.Text to ""
    2. Ustaw domyślne pozycje Spinnerów:
      set spnGatunek.Selection to "Literatura piękna"
      set spnStatus.Selection to "Do przeczytania"
      set spnOcena.Selection to "Bez oceny"
    3. Zresetuj stan sterowania edycją:
      set global indeksEdycji to 0
      set btnZapisz.Text to "💾 Zapisz książkę"
      set btnUsun.Enabled to False
  • Podłącz procedurę pod przycisk: when btnNowa.Click do call wyczyscFormularz.
Baza książek - slajd 25/45
Etap 4: Wybór książki z listy i załadowanie do edycji
Cel slajdu: obsługa zdarzenia ListView.AfterPicking i przeniesienie wybranego rekordu do pól formularza.

Krok 17: Zdarzenie lvKsiazki.AfterPicking (Podgląd i Edycja)

  • Z szuflady lvKsiazki wybierz blok: when lvKsiazki.AfterPicking do:
    1. Pobierz numer klikniętego wiersza:
      set global indeksEdycji to lvKsiazki.SelectionIndex
    2. Sprawdź poprawność indeksu: if get global indeksEdycji > 0 then:
      • Pobierz słownik wybranej książki z bazy:
        wybranaKsiazka = select list item (
            list = get global bazaKsiazek,
            index = get global indeksEdycji
        )
      • Załaduj dane do formularza:
        set txtISBN.Text to get value for key "isbn" in (wybranaKsiazka)
        set txtTytul.Text to get value for key "tytul" in (wybranaKsiazka)
        set txtAutor.Text to get value for key "autor" in (wybranaKsiazka)
        set txtWydawnictwo.Text to get value for key "wydawnictwo" in (wybranaKsiazka)
        set txtRok.Text to get value for key "rok" in (wybranaKsiazka)
        set txtStrony.Text to get value for key "strony" in (wybranaKsiazka)
        set spnGatunek.Selection to get value for key "gatunek" in (wybranaKsiazka)
        set spnStatus.Selection to get value for key "status" in (wybranaKsiazka)
        set spnOcena.Selection to get value for key "ocena" in (wybranaKsiazka)
        set txtNotatka.Text to get value for key "notatka" in (wybranaKsiazka)
      • Zmień stan przycisków:
        set btnZapisz.Text to "💾 Zaktualizuj wpis"
        set btnUsun.Enabled to True
Baza książek - slajd 26/45
Etap 4: Aktualizacja i bezpieczne usuwanie z bazy (dialog Notifier)
Cel slajdu: dokończenie logiki edycji istniejącej pozycji oraz implementacja bezpiecznego usuwania z potwierdzeniem modalnym.

Krok 18: Zastępowanie rekordu i modalne usuwanie

  • Aktualizacja w zdarzeniu btnZapisz.Click (gałąź else warunku indeksEdycji = 0):
    replace list item (
        list = get global bazaKsiazek,
        index = get global indeksEdycji,
        replacement = get local nowyRekord
    )
    call Notifier1.ShowAlert("Zaktualizowano dane książki!")
  • Bezpieczne usuwanie książki:
    1. Zdarzenie when btnUsun.Click do:
      call Notifier1.ShowChooseDialog(
          message = "Czy na pewno chcesz bezpowrotnie usunąć tę książkę?",
          title = "Potwierdzenie usunięcia",
          button1Text = "Tak, usuń",
          button2Text = "Anuluj",
          cancelable = True
      )
    2. Obsługa wyboru w when Notifier1.AfterChoosing(choice) do:
      if choice = "Tak, usuń" then
          remove list item (list = get global bazaKsiazek, index = get global indeksEdycji)
          call TinyDB1.StoreValue(tag = get global NAZWA_TAGU, valueToStore = get global bazaKsiazek)
          call odswiezListView
          call przeliczStatystyki
          call wyczyscFormularz
          call Notifier1.ShowAlert("Książka została usunięta z bazy.")
Baza książek - slajd 27/45
Etap 5: Skanowanie kodu paskowego aparatem (BarcodeScanner)
Cel slajdu: wykorzystanie aparatu fotograficznego urządzenia do optycznego odczytu kodu kreskowego z okładki.

Krok 19: Optyczny odczyt kodu kreskowego z okładki

  • Z szuflady btnSkanuj wybierz blok: when btnSkanuj.Click do:
    call BarcodeScanner1.DoScan
  • Z szuflady BarcodeScanner1 wybierz blok zdarzenia: when BarcodeScanner1.AfterScan(result) do:
    1. Wpisz odczytany ciąg znaków do pola ISBN:
      set txtISBN.Text to get result
    2. Wyświetl potwierdzenie:
      call Notifier1.ShowAlert(join("Zeskanowano kod: ", get result))
    3. Opcjonalnie: natychmiast wywołaj automatyczne zapytanie sieciowe!
      call pobierzDaneZSieci
Pamiętaj: Warunkiem koniecznym jest odznaczenie opcji UseExternalScanner (False) w oknie Designer dla komponentu BarcodeScanner1!
Baza książek - slajd 28/45
Etap 5: Zrozumienie kodów ISBN-10 i ISBN-13 oraz normalizacja
Cel slajdu: zrozumienie struktury identyfikatorów książek i przygotowanie procedury czyszczącej numer przed wysłaniem zapytania.

Krok 20: Struktura kodu ISBN i normalizacja ciągu znaków

  • Formaty numerów ISBN:
    • ISBN-13 (EAN-13): 13 cyfr z prefiksem wydawniczym 978 lub 979 (np. 978-83-08-06455-9). Taki kod jest wydrukowany nad kodem paskowym na okładce.
    • ISBN-10: starszy standard 10-cyfrowy (np. 83-08-06455-8).
  • Problem myślników i spacji: Wydawcy często drukują numery z myślnikami. Niektóre czytniki lub użytkownicy wpisują spacje. Serwery API wymagają czystego ciągu cyfr: 9788308064559.
  • Algorytm normalizacji numeru ISBN: Przed wysłaniem zapytania do sieci usuwamy wszelkie myślniki i spacje:
    czystyISBN = replace all text (
        text = replace all text(text = txtISBN.Text, segment = "-", replacement = ""),
        segment = " ",
        replacement = ""
    )
Baza książek - slajd 29/45
Etap 5: Architektura Open Library API i protokół HTTP
Cel slajdu: zrozumienie zasad działania otwartego sieciowego interfejsu programistycznego Open Library.

Krok 21: Usługa Open Library REST API

  • Czym jest Open Library? To globalny, otwarty projekt fundacji non-profit Internet Archive, którego celem jest stworzenie strony internetowej dla każdej opublikowanej książki.
  • Dlaczego Open Library API jest idealne do zajęć laboratoryjnych?
    • Jest w 100% bezpłatne i otwarte dla społeczności akademickiej.
    • Nie wymaga rejestracji ani kluczy API (w przeciwieństwie np. do Google Books API, które wymaga klucza deweloperskiego i narzuca limity zapytań — po ich przekroczeniu odpowiada błędem HTTP 429 Too Many Requests).
  • Adres bazowy endpointu dla zapytań o numer ISBN:
    https://openlibrary.org/isbn/{numer_isbn}.json
  • Przykład rzeczywistego zapytania: Dla książki Jacka Dukaja "Katedra" (ISBN 9788308064559):
    https://openlibrary.org/isbn/9788308064559.json
Baza książek - slajd 30/45
Etap 5: Wysłanie zapytania HTTP GET za pomocą komponentu Web
Cel slajdu: zaimplementowanie procedury wysyłającej żądanie sieciowe z telefonu do serwera API.

Krok 22: Procedura pobierzDaneZSieci

  • Utwórz procedurę: to procedure pobierzDaneZSieci:
    1. Oczyść numer ISBN z myślników i spacji do zmiennej lokalnej czystyISBN.
    2. Sprawdź, czy pole nie jest puste:
      if is empty (czystyISBN) then
          call Notifier1.ShowAlert("Najpierw wpisz lub zeskanuj kod ISBN!")
          // przerwij wykonywanie
    3. Zbuduj dynamiczny adres URL zapytania blokiem join:
      set Web1.Url to join("https://openlibrary.org/isbn/", czystyISBN, ".json")
    4. Wyślij asynchroniczne żądanie HTTP metodą GET:
      call Web1.Get
    5. Wyświetl dymek informacyjny:
      call Notifier1.ShowAlert("Łączenie z bazą Open Library...")
  • Podłącz procedurę pod przycisk: when btnSzukajSiec.Click do call pobierzDaneZSieci.
Baza książek - slajd 31/45
Etap 5: Asynchroniczny odbiór odpowiedzi w Web.GotText
Cel slajdu: poprawna obsługa kodów odpowiedzi protokołu HTTP i diagnostyka błędów sieciowych.

Krok 23: Zdarzenie Web1.GotText i kody HTTP

  • Z szuflady Web1 wybierz blok zdarzenia: when Web1.GotText(url, responseCode, responseType, responseContent) do:
    • responseCode: numeryczny kod statusu odpowiedzi HTTP (np. 200, 404, 500).
    • responseContent: surowy tekst dokumentu JSON zwrócony przez serwer.
  • Struktura warunkowa weryfikacji statusu serwera:
    if responseCode = 200 then
        // SUKCES: serwer odnalazł książkę i przesłał JSON
        call przetworzOdpowiedzJSON(responseContent)
    else if responseCode = 404 then
        // BRAK REKORDU: książki nie ma w bazie Open Library
        call Notifier1.ShowAlert("Nie znaleziono książki w katalogu Open Library. Wpisz dane ręcznie.")
    else
        // INNY BŁĄD SIECIOWY
        call Notifier1.ShowAlert(join("Błąd połączenia. Kod HTTP: ", responseCode))
Kod 200 OK oznacza sukces, kod 404 Not Found oznacza brak danego ISBN w bazie, a kod 500 błąd wewnętrzny serwera. Pamiętaj: gdy telefon w ogóle nie ma połączenia z Internetem, zdarzenie GotText nie zostanie wywołane — błąd sieciowy zgłosi wtedy system przez zdarzenie Screen1.ErrorOccurred (kod 1101).
Baza książek - slajd 32/45
Etap 5: Dekodowanie formatu JSON (JsonTextDecodeWithDictionaries)
Cel slajdu: zrozumienie konwersji surowego tekstu JSON na strukturę zagnieżdżonych słowników w App Inventor.

Krok 24: Zrozumienie i dekodowanie formatu JSON

  • Przykładowy dokument JSON zwracany przez Open Library:
    {
      "title": "Crime and punishment",
      "publish_date": "2003",
      "publishers": ["Penguin Classics"],
      "number_of_pages": 671,
      "by_statement": "Fyodor Dostoyevsky"
    }
  • Jak przetłumaczyć surowy tekst na słownik AI2? W szufladzie Web znajduje się dedykowany blok:
    call Web1.JsonTextDecodeWithDictionaries(jsonText = responseContent)
  • Zasada działania dekodera:
    • Obiekty JSON { ... } zamienia automatycznie na słowniki App Inventor (Dictionaries).
    • Tablice JSON [ ... ] zamienia na listy App Inventor (Lists).
    • Napisy i liczby mapuje bezpośrednio na typy bazowe.
Baza książek - slajd 33/45
Etap 5: Ekstrakcja danych i automatyczne uzupełnienie formularza
Cel slajdu: wydobycie poszczególnych pól (tytuł, autor, wydawnictwo, rok, liczba stron) ze słownika JSON i przypisanie do kontrolek UI.

Krok 25: Procedura przetworzOdpowiedzJSON

  • Utwórz procedurę: to procedure przetworzOdpowiedzJSON(jsonText):
    1. Zdekoduj tekst do zmiennej lokalnej:
      dict = call Web1.JsonTextDecodeWithDictionaries(jsonText = jsonText)
    2. Pobierz tytuł (klucz "title"):
      set txtTytul.Text to get value for key "title" in dictionary (dict) or "Nieznany tytuł"
    3. Pobierz rok wydania (klucz "publish_date"):
      set txtRok.Text to get value for key "publish_date" in dictionary (dict) or ""
    4. Pobierz liczbę stron (klucz "number_of_pages"):
      if is in dictionary? key "number_of_pages" in (dict) then
          set txtStrony.Text to get value for key "number_of_pages" in dictionary (dict)
      Uwaga: nie każdy rekord w katalogu zawiera liczbę stron; przy braku klucza pole pozostaje puste.
    5. Pobierz wydawcę (pierwszy element listy pod kluczem "publishers"):
      if is in dictionary? key "publishers" in (dict) then
          set txtWydawnictwo.Text to select list item (
              list = get value for key "publishers" in dictionary (dict),
              index = 1
          )
    6. Pobierz autora (pole "by_statement", jeśli istnieje):
      if is in dictionary? key "by_statement" in (dict) then
          set txtAutor.Text to get value for key "by_statement" in dictionary (dict)
    7. Poinformuj o sukcesie: call Notifier1.ShowAlert("Dane książki pobrane z Internetu!").
Baza książek - slajd 34/45
Etap 6: Wyszukiwanie i filtrowanie zbioru (ShowFilterBar)
Cel slajdu: omówienie działania paska wyszukiwania w ListView oraz implementacja własnego filtra gatunkowego.

Krok 26: Dwa poziomy wyszukiwania w aplikacji

  • Poziom 1: Natywny pasek wyszukiwania (ShowFilterBar):
    • Dzięki włączonej właściwości ShowFilterBar = True na szczycie komponentu lvKsiazki znajduje się interaktywny pasek wyszukiwania.
    • Wpisanie fragmentu nazwiska (np. Dukaj) lub słowa z tytułu (np. Katedra) natychmiast zawęża widoczne wiersze na ekranie.
    • Działa w czasie rzeczywistym, bez konieczności pisania ani jednego dodatkowego bloku!
  • Poziom 2: Własna procedura zaawansowanego filtrowania: Gdybyśmy chcieli filtrować książki według ukrytych pól (np. po dokładnym numerze ISBN lub prywatnej notatce), stosujemy algorytm pętli:
    to procedure filtrujPoGatunku(szukanyGatunek)
        listaPrzefiltrowana = create empty list
        for each k in list (get global bazaKsiazek)
            if get value for key "gatunek" in (k) = szukanyGatunek then
                add items to list (listaPrzefiltrowana, ...zbuduj napis...)
        set lvKsiazki.Elements to listaPrzefiltrowana
Uwaga: nadpisanie listy lvKsiazki.Elements przefiltrowanymi wierszami zmienia odwzorowanie SelectionIndex. Po filtrowaniu numer wiersza nie odpowiada już pozycji w bazaKsiazek, więc zdarzenie AfterPicking z podglądem i edycją (krok 17) zadziała poprawnie dopiero po przywróceniu pełnej listy procedurą odswiezListView.
Baza książek - slajd 35/45
Etap 6: Moduł statystyk czytelnika i analityka danych
Cel slajdu: zaimplementowanie algorytmu agregacji danych: zliczanie książek, obliczanie średniej ocen i profilu czytelnika.

Krok 27: Procedura przeliczStatystyki

  • Utwórz procedurę: to procedure przeliczStatystyki:
    1. Zainicjalizuj zmienne lokalne wewnątrz procedury:
      lacznie = length of list (get global bazaKsiazek)
      przeczytane = 0
      sumaOcen = 0
      licznikOcen = 0
    2. Pętla po wszystkich pozycjach w global bazaKsiazek:
      • Jeśli get value for key "status" in (k) = "Przeczytana", zwiększ przeczytane o 1.
      • Wyciągnij ocenę: jeśli ocena zawiera gwiazdki, wyciągnij cyfrę (np. "5" z napisu "(5/5)") lub sprawdź blokiem tekstowym. Dodaj wartość do sumaOcen i zwiększ licznikOcen o 1.
    3. Oblicz średnią arytmetyczną z zabezpieczeniem przed dzieleniem przez zero:
      srednia = if licznikOcen > 0 then
          round( (sumaOcen / licznikOcen) * 10 ) / 10
      else
          "-"
    4. Uaktualnij etykietę:
      set lblStatystyki.Text to join(
          "Łącznie: ", lacznie,
          " | Przeczytane: ", przeczytane,
          " | Średnia ocen: ", srednia, "/5"
      )
Baza książek - slajd 36/45
Etap 6: Udostępnianie recenzji i rekomendacji (Sharing)
Cel slajdu: wykorzystanie natywnych funkcji społecznościowych telefonu do dzielenia się opinią o książce.

Krok 28: Moduł społecznościowy (Sharing1)

  • Użytkownik przeczytał wybitną książkę i chce jednym kliknięciem polecić ją znajomym.
  • Z szuflady btnUdostepnij wybierz blok: when btnUdostepnij.Click do:
    1. Sprawdź, czy wybrano książkę z bazy:
      if get global indeksEdycji = 0 then
          call Notifier1.ShowAlert("Najpierw kliknij książkę na liście, którą chcesz polecić!")
          // przerwij wykonywanie
    2. Zbuduj atrakcyjny tekst wiadomości blokiem join:
      trescWiadomosci = join(
          "Cześć! Chcę polecić Ci znakomitą książkę:
      
      ",
          "📖 „", txtTytul.Text, "”
      ",
          "✍️ Autor: ", txtAutor.Text, "
      ",
          "⭐ Moja ocena: ", spnOcena.Selection, "
      
      ",
          "💬 Moja recenzja:
      ", txtNotatka.Text, "
      
      ",
          "Wysłano z aplikacji Moja Biblioteczka."
      )
    3. Wywołaj natywne udostępnianie w systemie Android:
      call Sharing1.ShareMessage(message = trescWiadomosci)
Komponent Sharing wywołuje systemowe okno Androida (Share Sheet), pozwalając wysłać recenzję przez SMS, e-mail, WhatsApp czy Messenger!
Baza książek - slajd 37/45
Etap 6: Eksport bazy do formatu CSV (arkusz kalkulacyjny)
Cel slajdu: zaimplementowanie funkcji eksportu danych do otwartego formatu CSV za pomocą wbudowanych bloków listowych.

Krok 29: Eksport bazy do formatu CSV

  • Format CSV (Comma-Separated Values) to uniwersalny standard tabelaryczny otwierany w programach Excel, LibreOffice Calc i Google Sheets.
  • W szufladzie Lists znajduje się potężny blok: list to csv table (list).
  • Implementacja w zdarzeniu when btnEksportCSV.Click do:
    1. Zainicjalizuj zmienną lokalną: tabelaCSV = create empty list.
    2. Dodaj wiersz nagłówkowy kolumn:
      add items to list (tabelaCSV, make a list(
          "ISBN", "Tytul", "Autor", "Wydawnictwo", "Rok", "Gatunek", "Status", "Ocena", "Data"
      ))
    3. Dla każdego słownika w bazie stwórz wiersz danych i dodaj do tabelaCSV:
      for each k in list (get global bazaKsiazek)
          add items to list (tabelaCSV, make a list(
              get value for key "isbn" in (k),
              get value for key "tytul" in (k),
              get value for key "autor" in (k),
              ...pozostałe pola...
          ))
    4. Skonwertuj tabelę na tekst CSV i udostępnij:
      tekstCSV = call list to csv table (list = tabelaCSV)
      call Sharing1.ShareMessage(message = tekstCSV)
Baza książek - slajd 38/45
Etap 7: Uruchomienie i testy w MIT AI2 Companion na smartfonie
Cel slajdu: praktyczne przetestowanie aplikacji na fizycznym telefonie z Androidem w czasie rzeczywistym.

Krok 30: Połączenie przez AI Companion na telefonie

  • Dlaczego fizyczny telefon, a nie emulator komputerowy? Emulator na komputerze PC nie posiada fizycznej kamery ze zmiennym autofokusem i nie potrafi zeskanować kodu kreskowego z rzeczywistej książki! Do tego ćwiczenia smartfon jest niezbędny.
  • Procedura parowania krok po kroku:
    1. Zainstaluj darmową aplikację MIT AI2 Companion ze sklepu Google Play na smartfonie.
    2. Upewnij się, że komputer i smartfon korzystają z tej samej sieci Wi-Fi (lub włącz tryb danych komórkowych i użyj kodu 6-znakowego).
    3. W przeglądarce w menu App Inventora kliknij: Connect -> AI Companion.
    4. Na ekranie komputera pojawi się okno z kodem QR oraz kodem 6-literowym.
    5. Uruchom aplikację na telefonie, dotknij Scan QR code i skieruj aparat na monitor.
    6. Po załadowaniu paska postępu aplikacja uruchomi się na żywo na Twoim smartfonie!
  • Uprawnienia kamery: Przy pierwszym kliknięciu przycisku Skanuj Android zapyta o zgodę na dostęp do aparatu – wybierz Zezwól podczas używania aplikacji.
Baza książek - slajd 39/45
Etap 7: Matryca testów funkcjonalnych i scenariusze brzegowe
Cel slajdu: przeprowadzenie rygorystycznych testów laboratoryjnych zgodnie z metodyką QA.

Krok 31: Protokół weryfikacji jakości (QA Matrix)

NrScenariusz testowyAkcja testeraOczekiwany rezultat
1Skanowanie kodu fizycznej książkiKliknij Skanuj, wyceluj w okładkęNumer ISBN pojawia się w polu txtISBN.
2Pobranie danych z Open LibraryKliknij Pobierz przy poprawnym ISBNPola Tytuł, Autor, Wydawnictwo uzupełniają się same.
3Walidacja pustego formularzaKliknij Zapisz przy pustym tytuleDymek błędu, baza nie przyjmuje pustego rekordu.
4Trwałość bazy po restarcieZapisz książkę, zamknij i włącz programZapisana książka natychmiast pojawia się na liście.
5Edycja istniejącego wpisuKliknij pozycję na liście, zmień ocenęLista odświeża się z nową liczbą gwiazdek.
6Bezpieczne usuwanie wpisuKliknij Usuń, potwierdź w oknie dialogowymKsiążka znika z bazy, statystyki przeliczają się.
7Zachowanie w trybie offlineWyłącz Wi-Fi, kliknij PobierzAplikacja nie ulega awarii, dymek systemowy informuje o błędzie połączenia.
Baza książek - slajd 40/45
Etap 7: Typowe błędy implementacyjne i diagnostyka problemów
Cel slajdu: analiza najczęstszych błędów popełnianych przez studentów oraz strategie ich szybkiego usuwania.

Krok 32: Jak radzić sobie z typowymi problemami

  • Usterka 1: Skaner nie działa i pojawia się komunikat o braku aplikacji.
    • Przyczyna: W oknie Designer dla BarcodeScanner1 pozostawiono zaznaczone pole UseExternalScanner = True.
    • Rozwiązanie: Odznacz tę opcję (False) w panelu Properties.
  • Usterka 2: Po pobraniu z sieci w polach pojawia się błąd o braku klucza.
    • Przyczyna: Zastosowano stary blok JsonTextDecode zamiast JsonTextDecodeWithDictionaries.
    • Rozwiązanie: Wymień blok dekodujący na nowoczesny blok ze słownikami.
  • Usterka 3: Po kliknięciu w pole tekstowe klawiatura zasłania przycisk Zapisz.
    • Przyczyna: Wyłączona właściwość Scrollable w Screen1.
    • Rozwiązanie: Zaznacz Scrollable = True we właściwościach ekranu głównego.
  • Usterka 4: Po obróceniu telefonu wpisane dane znikają.
    • Rozwiązanie: Ustaw właściwość ScreenOrientation = Portrait w Screen1.
Baza książek - slajd 41/45
Etap 7: Dobre praktyki inżynierii oprogramowania w App Inventor
Cel slajdu: podsumowanie zasad czystego kodu (Clean Code), refaktoryzacji i modularyzacji w projektach mobilnych.

Krok 33: Czysty kod i higiena projektu (Clean Code)

  • 1. Zasada DRY (Don't Repeat Yourself): Nigdy nie kopiuj tych samych bloków odświeżania widoku czy czyszczenia pól! Zamykaj powtarzalne operacje w dedykowanych procedurach (odswiezListView, wyczyscFormularz, przeliczStatystyki).
  • 2. Semantyczne nazewnictwo: Stosuj rygorystycznie prefiksy kontrolek (txt..., btn..., spn..., lbl..., va..., ha..., lv...). W oknie Blocks natychmiast rozpoznasz typ komponentu.
  • 3. Komentarze w blokach logicznych: Kliknij prawym przyciskiem myszy na blok procedury i wybierz Add Comment. Krótki opis intencji ułatwi pracę Tobie i prowadzącemu ocenę.
  • 4. Kopia bezpieczeństwa (.aia): Po każdym udanym etapie pobierz kod źródłowy na dysk komputera: Projects -> Export selected project (.aia) to my computer.
Plik .aia to Twój kod źródłowy! Chroń go przed utratą. Plik .apk to jedynie wynik kompilacji, z którego nie da się łatwo odzyskać bloków.
Baza książek - slajd 42/45
Etap 8: Konfiguracja pakietu, ikona i budowanie pliku APK
Cel slajdu: ostateczne przygotowanie aplikacji do instalacji produkcyjnej i wygenerowanie instalatora Androida.

Krok 34: Kompilacja instalatora aplikacji (.apk)

  • Ostatnie szlify w oknie Designer (właściwości Screen1):
    • Icon: Wgraj estetyczną grafikę PNG (np. 512x512 px) przedstawiającą książkę lub regał biblioteczny.
    • AppName: Upewnij się, że wpisano: Moja Biblioteczka.
    • VersionCode: 1, VersionName: 1.0.
  • Kompilacja do pliku APK:
    1. W górnym menu kliknij: Build -> Android App (.apk).
    2. Serwer chmurowy MIT skompiluje projekt: przetłumaczy bloki na kod Java, dołączy zasoby, skompiluje kod bajtowy Dalvik/ART i podpisze cyfrowo pakiet APK.
    3. Po osiągnięciu 100% pojawi się okno z kodem QR do bezpośredniego pobrania pliku na telefon oraz przycisk Download .apk to my computer.
  • Instalacja na smartfonie: Zezwól w ustawieniach systemu Android na instalowanie aplikacji z tego źródła (nieznane źródła) i ciesz się w pełni samodzielną aplikacją!
Baza książek - slajd 43/45
Podsumowanie architektury aplikacji i poznanych komponentów
Cel slajdu: syntetyczne zestawienie warstw architektonicznych i przepływu danych w zrealizowanym projekcie.

Kompletna mapa technologiczna projektu

  • 1. Warstwa prezentacji (UI & Layout): Screen1 (Scrollable), VerticalArrangement, HorizontalArrangement, TextBox (MultiLine, NumbersOnly), Spinner (słowniki pojęć), Button, ListView (ShowFilterBar).
  • 2. Warstwa logiki biznesowej: Zmienne globalne stanu (bazaKsiazek, indeksEdycji), procedury CRUD, walidacja danych (trim, is a number?), normalizacja numeru ISBN.
  • 3. Warstwa integracji sprzętowej: BarcodeScanner (wbudowana optyczna detekcja kodów paskowych kamerą), Clock (stemplowanie daty wpisu).
  • 4. Warstwa integracji sieciowej: Web (asynchroniczny protokół HTTP GET, obsługa kodów statusu 200/404, parser JsonTextDecodeWithDictionaries).
  • 5. Warstwa trwałości danych (Persistence): TinyDB (serializacja list słowników do pamięci trwałej smartfona).
  • 6. Warstwa integracji systemowej: Sharing (Android Intents - udostępnianie recenzji i eksport CSV), Notifier (dialogi modalne i toasty).
Baza książek - slajd 44/45
Zadania laboratoryjne i pytania kontrolne dla studentów
Cel slajdu: weryfikacja zrozumienia pojęć oraz samodzielne zadania programistyczne na ocenę celującą.

Sprawdź swoją wiedzę i wykonaj zadania

  1. 1. Dlaczego pojedynczy rekord książki reprezentujemy jako słownik (Dictionary), a całą bazę jako listę słowników?
    Słownik pozwala na dostęp do pól przez czytelne nazwy kluczy (np. "tytul", "rok") w czasie stałym O(1), jest odporny na dodawanie nowych atrybutów bez psucia indeksów oraz bezpośrednio odpowiada strukturze formatu JSON. Lista słowników pozwala na dynamiczne zarządzanie zbiorem wielu takich rekordów (dodawanie, usuwanie, pętla for each).
  2. 2. Dlaczego w komponencie BarcodeScanner ustawiamy właściwość UseExternalScanner = False?
    Ustawienie wartości False wymusza użycie wbudowanej w aplikację biblioteki ZXing. Dzięki temu kamera otwiera się bezpośrednio wewnątrz naszej aplikacji i nie wymaga instalowania zewnętrznych programów skanujących ze sklepu Google Play.
  3. 3. Wyjaśnij, dlaczego odpowiedź sieciową obsługujemy w zdarzeniu Web1.GotText, a nie bezpośrednio po wywołaniu Web1.Get.
    Metoda Web1.Get jest asynchroniczna – wysyła pakiet sieciowy w tle i natychmiast kończy działanie, aby nie zablokować interfejsu telefonu. Odpowiedź z serwera nadejdzie po pewnym czasie (np. kilkaset milisekund) i dopiero wtedy system generuje zdarzenie Web1.GotText z odebranymi danymi.
  4. 4. Dlaczego usuwanie książki realizujemy w zdarzeniu Notifier1.AfterChoosing, a nie w btnUsun.Click?
    Metoda ShowChooseDialog jedynie wyświetla okno modalne na ekranie. Decyzja użytkownika (kliknięcie "Tak, usuń" lub "Anuluj") następuje później i jest przekazywana jako parametr do zdarzenia Notifier1.AfterChoosing. Usunięcie rekordu w Click usunęłoby książkę przed podjęciem decyzji przez człowieka.
Zadania laboratoryjne na ocenę celującą (wybierz co najmniej jedno):
  • Zadanie A: Dodaj do formularza pole "Lokalizacja na półce" (np. Regał 1, Półka B) i uwzględnij je w wyszukiwaniu oraz eksporcie CSV.
  • Zadanie B: Zaimplementuj na komponencie Canvas wykres słupkowy prezentujący liczbę przeczytanych książek z podziałem na gatunki.
  • Zadanie C: Zintegruj komponent Camera, aby umożliwić zrobienie własnego zdjęcia okładki i zapisanie ścieżki do pliku w słowniku książki.
Baza książek - slajd 45/45
Zakończenie ćwiczenia, literatura i dalsze perspektywy rozwoju
Cel slajdu: podsumowanie zajęć laboratoryjnych, wykaz bibliografii oraz wskazanie zaawansowanych kierunków rozwoju aplikacji.

Podsumowanie i perspektywy rozwoju

  • Osiągnięty rezultat: Zbudowaliśmy kompletną, autonomiczną aplikację mobilną łączącą lokalną bazę danych TinyDB, skaner kodów kreskowych, internetowe API Open Library, narzędzia analityczne oraz moduł udostępniania i eksportu.
  • Dalsze kierunki rozwoju aplikacji (dla ambitnych):
    • Synchronizacja w chmurze: Zastąpienie TinyDB komponentem CloudDB lub FirebaseDB w celu współdzielenia bazy domowej z pozostałymi domownikami w czasie rzeczywistym.
    • Relacyjna baza SQLite: Zastosowanie rozszerzenia SQLite dla App Inventora w celu implementacji relacji wiele-do-wielu (autorzy, wydawnictwa, wypożyczenia znajomym).
    • Sztuczna inteligencja (OCR): Rozpoznawanie tekstu z fotografii okładki za pomocą rozszerzeń widzenia maszynowego (Computer Vision).
  • Literatura i materiały źródłowe:
    • Materiały wykładowe: pam1.pdf, pam2.pdf, pam3.pdf, pam4.pdf, pam5.pdf.
    • Oficjalna dokumentacja MIT App Inventor: ai2.appinventor.mit.edu/reference/.
    • Dokumentacja Open Library Books API: openlibrary.org/developers/api.
    • Wolber D., Abelson H., Spertus E., Looney L.: App Inventor 2: Create Your Own Android Apps, O'Reilly Media.