Lista 5.

Aplikacje mobilne, Laboratorium

by Jerry Sky


Zadanie 1.

Napisz prostą dwuwymiarową grę w tenisa Pong lub Arkanoid. Wykorzystaj SurfaceView, który będzie odpowiedzialny za rysowanie gry. Wszystkie informacje o stanie gry mają być przechowywane w bazie SQLite z wykorzystaniem Room-a.

Pliki projektu znajdują się w katalogu ex-1/Pingouin.


Zadanie 2.

Celem tego zadania jest wykorzystanie bazy danych czasu rzeczywistego Firebase. Można wykorzystać ją w dowolnej aplikacji, gdzie jest logowanie na konto i komunikacja czasu rzeczywistego przez sieć.

Przykłady:

Rozwiązanie zawiera prostą aplikację do rozmów między użytkownikami. Każdy(a) użytkownik(czka) musi się zalogować przy pomocy adresu e-mail i hasła.

Pliki projektu znajdują się w katalogu ex-2/Discorde.

Odnośnie kwestii autoryzacji

Aktualne rozwiązanie nie jest w pełni bezpieczne pod względem podszywania się pod innych użytkowników. Aplikacja sama dołącza pole author do wysyłanej wiadomości — serwer akceptuje tę wartość bez sprawdzania.

Rozwiązaniem byłoby uruchomienie funkcjonalności Firebase Functions, która pozwalałaby na dodanie funkcji nasłuchującej zmian w bazie danych Firebase Realtime DB. Funkcja ta pobierałaby faktyczną nazwę wyświetlaną użytkownika(czki) wysyłającego(ej) wiadomość, a następnie nadpisywała tę wartość wysłaną przez aplikację.

W ten sposób zapewniamy, że wiadomość ma faktyczny podpis tego użytkownika(czki) w bazie danych.

Szkic funkcji:

import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { Message } from '../model/message'

export const onMessageCreate = functions.database.ref('/path')
    .onCreate(async (snap, context) => {
        // get the message that was created
        const message = snap.val() as Message
        // get the ID of the user that created the message
        const userId = context.auth.uid

        // get the user’s actual display name
        const userData = await admin.auth()
            .getUser(userId)
        const userDisplayName = userData.displayName

        const newMessageValue: Partial<Message> = {
            author: userDisplayName
        }

        // update the author
        return snap.ref.update(newMessageValue)
    })

Oczywiście, w pełnoprawnej aplikacji nie zapisywalibyśmy wyświetlanej nazwy użytkownika, a raczej jego identyfikator (uid). Wówczas serwer mógłby działać w ten sposób, że za każdym razem, kiedy pojawia się nowe zapytanie o listę wiadomości, serwer zwraca listę z zamienionymi uid na nazwy wyświetlane użytkowników.
Nie mielibyśmy wtedy oczywistego problemu duplikowania danych i niepotrzebnej redundancji.