Podczas zajęć skupimy się na tworzeniu aplikacji mobilnej w środowisku Android Studio, poznając podstawowy workflow pracy programisty Androida: od utworzenia projektu, przez budowę interfejsu, po obsługę danych w aplikacji.
Jako praktyczny przykład zastosowania aplikacji wykorzystamy scenariusz pracy z urządzeniem mobilnym wyposażonym w skaner kodów (np. Zebra TC51), co pozwoli zobaczyć, jak aplikacje Android współpracują z realnym sprzętem wykorzystywanym w logistyce, handlu czy przemyśle.
Skaner posłuży tu jako źródło danych, a głównym celem będzie zaprojektowanie aplikacji, która poprawnie odbiera, przetwarza i wykorzystuje te dane w praktycznym kontekście.

Cele lekcji

Po tej lekcji:

  • wiesz, czym różni się „aparat + biblioteka skanera” od skanera sprzętowego Zebry,
  • rozumiesz, co to jest DataWedge i po co jest,
  • potrafisz zrobić aplikację, która odbiera zeskanowany kod jako tekst (najprostsza integracja),
  • wiesz, kiedy potrzebne jest Zebra SDK (EMDK) i co wtedy zyskujesz,
  • masz plan: emulator → test → telefon/TC51.

Krok 1. Instalacja i konfiguracja Android Studio

– pobranie i instalacja


– pierwsze uruchomienie


– co to jest SDK i emulator

Co to jest emulator?

Emulator to wirtualne urządzenie z Androidem, które działa na komputerze i zachowuje się jak prawdziwy telefon lub tablet.

Emulator pozwala:

  • uruchamiać i testować aplikację bez fizycznego telefonu,
  • sprawdzić, jak aplikacja wygląda i działa,
  • debugować błędy krok po kroku.

Dzięki emulatorowi możemy pisać i testować aplikację nawet wtedy, gdy nie mamy jeszcze fizycznego urządzenia, np. skanera Zebra.


Dlaczego to ważne na tym etapie?

Emulator umożliwia ich testowanie.
– utworzenie pierwszego projektu

SDK umożliwia tworzenie aplikacji.

Wybieramy empty activity

Nazywamy aplikację. Ta nazwa, będzie widoczna na telefonie/emulatorze i pojawi się później w Google Play (jeśli opublikujesz).

Plik MainActivity.kt – co to jest i za co odpowiada?

MainActivity.kt to główny plik startowy aplikacji.
To właśnie od tej klasy Android zaczyna działanie programu po uruchomieniu aplikacji.

Można powiedzieć, że:

MainActivity to „punkt wejścia” aplikacji Android.

Struktura pliku – krok po kroku
1️⃣ Deklaracja pakietu
package pl.edu.infa.scanapp


Określa, do jakiego pakietu należy plik.

Musi być zgodna z nazwą pakietu projektu.

Zapewnia unikalność aplikacji w systemie Android.

2️⃣ Importy
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent


Importy to gotowe klasy i funkcje, z których korzysta aplikacja.

Dzięki nim Android Studio „wie”, skąd pochodzi używany kod.

Na tym etapie nie musimy ich jeszcze dokładnie znać — wystarczy świadomość, że są potrzebne.

3️⃣ Definicja klasy MainActivity
class MainActivity : ComponentActivity() {


Definiujemy klasę o nazwie MainActivity.

ComponentActivity to podstawowa klasa Androida, która:

obsługuje ekran,

reaguje na cykl życia aplikacji.

Każda aplikacja Android ma co najmniej jedną Activity.

4️⃣ Metoda onCreate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)


onCreate to metoda, która:

wykonuje się przy starcie aplikacji,

służy do inicjalizacji ekranu i logiki.

super.onCreate(...) wywołuje kod systemowy Androida — zawsze musi tu być.

5️⃣ setContent { ... } – co widzimy na ekranie
setContent {
ScanAppTheme {
// zawartość ekranu
}
}



setContent określa co zostanie wyświetlone na ekranie.

W nowoczesnych projektach Android używa się Jetpack Compose:

zamiast plików XML,

interfejs tworzy się w Kotlinie.

Na razie traktujemy to jako:

„Tutaj definiujemy wygląd ekranu aplikacji.”

Co warto zapamiętać na tym etapie

MainActivity.kt to centrum sterowania aplikacją.

onCreate() uruchamia się przy starcie aplikacji.

To tutaj później:

- odbierzemy dane (np. ze skanera),

- zareagujemy na zdarzenia,

- zmienimy zawartość ekranu.

Dlaczego to ważne przed pracą ze skanerem?

Bo skaner (Zebra) nie działa sam, dane ze skanera trafią właśnie do Activity, zanim odbierzemy skan, musimy rozumieć:

- gdzie jesteśmy w kodzie,

- co uruchamia aplikację.

Pierwsza drobna zmiana w MainActivity.kt

1️⃣ Znajdź fragment setContent { ... }

W pliku MainActivity.kt zobaczysz coś w tym stylu (nazwy mogą się minimalnie różnić):

setContent {
    ScanAppTheme {
        Greeting("Android")
    }
}

albo (w nowszej wersji):

setContent {
    ScanAppTheme {
        Surface(
            modifier = Modifier.fillMaxSize()
        ) {
            Greeting(
                name = "Android",
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}


2️⃣ Zmień tekst wyświetlany na ekranie

Zamień "Android" na własny tekst, np.:

Greeting("ScanApp – pierwsza aplikacja")

czyli finalnie np.:

Greeting("ScanApp – pierwsza aplikacja")

To jedna linijka, a zmiana będzie widoczna od razu po uruchomieniu.


3️⃣ Uruchom aplikację

  • Kliknij ▶ Run (zielona strzałka)
  • Wybierz emulator
  • Poczekaj, aż aplikacja się uruchomi

👉 Na ekranie emulatora zobaczysz nowy tekst, który sam wpisałeś.


Co właśnie się wydarzyło (bardzo ważne)

  • Zmieniłeś kod w Kotlinie
  • Android Studio:
    • zbudowało aplikację,
    • uruchomiło ją na emulatorze,
    • wyświetliło nową treść.

To dokładnie ten sam mechanizm, który później:

  • pokaże zeskanowany kod,
  • zaktualizuje ekran po skanowaniu,
  • zareaguje na dane z urządzenia Zebra.

Jeśli chcesz jeszcze bardziej „czytelny” efekt (opcjonalnie)

Możemy usunąć wszystko i wpisać własny tekst od zera:

setContent {
    Text("Witaj! To moja pierwsza aplikacja w Android Studio")
}


Dodanie pierwszego przycisku (Jetpack Compose)

1️⃣ Znajdź miejsce w Scaffold

Masz teraz coś w tym stylu:

Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
    Greeting(
        name = "ScanApp",
        modifier = Modifier.padding(paddingValues = innerPadding)
    )
}


2️⃣ Zamień Greeting(...) na tekst + przycisk

Wstaw ten kod:

Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
    Column(
        modifier = Modifier
            .padding(innerPadding)
            .padding(16.dp)
    ) {

        Text(
            text = "To jest moja pierwsza aplikacja",
        )

        Spacer(modifier = Modifier.height(16.dp))

        Button(
            onClick = {
                // tu później dodamy akcję (np. skanowanie)
            }
        ) {
            Text("Kliknij mnie")
        }
    }
}


3️⃣ Jeśli Android Studio podkreśli coś na czerwono

Użyj:

  • Alt + EnterImport

Najczęściej potrzebne importy:

  • Button
  • Text
  • Column
  • Spacer
  • Modifier
  • dp

Android Studio zrobi to automatycznie.


4️⃣ Uruchom aplikację ▶

Kliknij Run i spójrz na emulator.

👉 Zobaczysz:

  • tekst,
  • przycisk „Kliknij mnie”,
  • po kliknięciu… jeszcze nic (i tak ma być).

Co właśnie zrobiłeś (ważne!)

  • Dodałeś pierwszy element interaktywny.
  • Poznałeś:
    • Button – przycisk,
    • onClick – reakcję na kliknięcie,
    • Column – układ pionowy.

To dokładnie ten mechanizm, którego użyjemy później:

  • przycisk „Skanuj”,
  • reakcja na skan z Zebry,
  • aktualizacja tekstu na ekranie.

Zmiana tekstu po kliknięciu (Compose – podstawy)

1️⃣ Dodaj zmienną stanu nad Scaffold

W setContent { … }, na samym początku, dodaj:

var message by remember { mutableStateOf("To jest moja pierwsza aplikacja") }

Całość zacznie wyglądać tak:

setContent {
    ScanAppTheme {

        var message by remember { mutableStateOf("To jest moja pierwsza aplikacja") }

        Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
            ...
        }
    }
}

Jeśli coś się podkreśli na czerwono → Alt + Enter → Import
(potrzebne: remember, mutableStateOf, by)

Jeżeli nie masz możliwości wykonania importów automatycznie, możesz dopisać je ręcznie w zaznaczonym miejscu.


2️⃣ Zmień Text, żeby korzystał ze zmiennej

Zamiast:

Text(
    text = "To jest moja pierwsza aplikacja"
)

daj:

Text(
    text = message
)


3️⃣ Zmień onClick przycisku

W Button zamień pusty onClick na:

Button(
    onClick = {
        message = "Przycisk został kliknięty!"
    }
) {
    Text(text = "Kliknij mnie")
}


4️⃣ Uruchom aplikację ▶

Kliknij Run i:

  • start → widzisz tekst początkowy
  • klik → tekst zmienia się natychmiast

Bez restartu, bez odświeżania.


Co właśnie zrobiłeś (to jest kluczowe)

  • Poznałeś stan (state) w Compose
  • Zrozumiałeś, że:
    • UI reaguje na zmianę danych
    • nie „rysujesz ekranu ręcznie”
  • To dokładnie ten mechanizm, który za chwilę wykorzystamy do:
    • pokazania zeskanowanego kodu,
    • komunikatu „skan udany”,
    • licznika, listy, historii skanów.

1) Co mamy: Zebra TC51, Android, skaner sprzętowy

Zebra TC51 ma wbudowany skaner (laser/imager). W praktyce są 3 drogi, żeby go użyć w aplikacji:

  1. DataWedge (najprościej)
    Skaner „wpisuje” tekst do aplikacji tak, jakbyś pisał klawiaturą, albo wysyła Intent do aplikacji.
  2. Zebra EMDK / Scanner SDK (bardziej „programistycznie”)
    Masz większą kontrolę: start/stop skanowania, konfiguracje, eventy, itp.
  3. Biblioteki kamerowe (np. ZXing / ML Kit)
    Dobre na zwykłe telefony — ale to nie wykorzystuje sprzętowego skanera Zebry tak wygodnie jak DataWedge/EMDK.

W tej lekcji robimy wariant 1, bo najszybciej daje efekt.


2) Android Studio: Kotlin czy Java?

  • Możecie pisać w Kotlinie: zwykle kod jest krótszy i czytelniejszy (mniej „boilerplate”).
  • Java też zadziała, ale jeśli Tymek zaczyna, Kotlin jest najczęściej wygodniejszy.

Wniosek do decyzji: robimy Kotlin w Android Studio.


3) Emulator — co da się zasymulować?

  • Emulator Androida nie zasymuluje sprzętowego skanera Zebry (to nie ten hardware).
  • Ale da się zasymulować to, co aplikacja dostaje:
    • albo „wpis” tekstu do pola (jakby DataWedge działał w trybie klawiatury),
    • albo „Intent” z danymi (symulujemy ręcznie przyciskiem „Test”).

Czyli: logikę odbioru i obsługi kodu testujemy na emulatorze, a prawdziwe skanowanie sprawdzamy na TC51.


4) Najprostszy plan aplikacji (DataWedge = tekst w polu)

Zadanie

Zrób aplikację z:

  • polem tekstowym „Kod”
  • listą zeskanowanych kodów (np. w pamięci)
  • przyciskiem „Dodaj / Zapisz” (na start może tylko dopisywać do listy)

Minimalny scenariusz testu na emulatorze

  • Klikasz w pole „Kod”
  • Wpisujesz ręcznie np. 5901234123457
  • Enter → aplikacja traktuje to jak „skan”

To 1:1 imituje tryb DataWedge „keystroke output”.


5) Wariant profesjonalny: DataWedge przez Intent

To jest wygodniejsze, bo:

  • nie musisz mieć kursora w polu,
  • dostajesz kod w zdarzeniu, możesz go od razu przetwarzać.

Co robicie koncepcyjnie

  • Aplikacja ma „nasłuch” (BroadcastReceiver / obsługa Intentów)
  • DataWedge na urządzeniu Zebry wysyła Intent z:
    • wartością skanu
    • typem symbologii (EAN-13, Code128, QR itd.)

Jak to testować bez TC51

  • Dodajecie w aplikacji przycisk „TEST INTENT”
  • Po kliknięciu aplikacja sama wywołuje tę samą ścieżkę kodu, którą normalnie uruchomi skaner.

6) Co to jest Zebra SDK / EMDK i po co

Zebra SDK (często spotkasz nazwę EMDK) warto ruszać, gdy:

  • chcesz pełną kontrolę nad skanerem,
  • chcesz zmieniać ustawienia skanera z poziomu aplikacji,
  • chcesz stabilne zachowanie w środowisku firmowym (magazyn, sklep).

Ale: na pierwszą lekcję to za dużo. Najpierw DataWedge, potem dopiero SDK.


7) Propozycja pracy domowej / kolejny krok

  1. Dodać walidację:
    • jeśli kod ma mniej niż np. 4 znaki → odrzucaj
  2. Dodać historię + „kopiuj do schowka”
  3. Dodać rozpoznawanie typu (EAN/QR) jeśli będziecie szli w Intent + DataWedge
  4. Dopiero potem: „tryb magazynowy” (np. liczenie sztuk, koszyk, stan)

8.
fun TimedMessageExample() {

var message by remember { mutableStateOf("Tekst początkowy") }
var showTempMessage by remember { mutableStateOf(false) }

// Efekt czasowy
LaunchedEffect(showTempMessage) {
    if (showTempMessage) {
        delay(2000)              // 2 sekundy
        message = "Tekst początkowy"
        showTempMessage = false
    }
}

Column {
    Text(
        text = message,
        fontSize = 20.sp
    )

    Button(
        onClick = {
            message = "Kliknięto przycisk!"
            showTempMessage = true
        }
    ) {
        Text("Kliknij")
    }
}

}