SqLite – typy danych oraz zapis daty

W Androidzie mamy dostępną wbudowaną relacyjną baze danych – SqLite. Jak można wnioskować po nazwie, ta baza danych jest trochę okrojona w porównaniu do baz takich jak Oracle, Sybase czy PostgreSQL.

Dostępne typy danych w SqlLite to:

  • INTEGER – służy do przechowywania liczb całkowitych; w zależności od wielkości wartości przechowywane są na 1, 2, 3, 4, 6 lub 8 bitach
  • REAL – liczby zmiennoprzecinkowe
  • TEXT- ciągi znaków
  • BLOB – dane binarne

Jak widać nie ma tego za wiele, lecz wystarczająco, aby zapewnić nam wszystko czego potrzebujemy. Przynajmniej w większości wypadków 🙂

Chociaż po chwili zastanowienia… gdzie jest typ Date? Jak zapisywać datę? Tyle pytań bez odpowiedzi… Aż do teraz!

Data może być przechowywana w bazie danych w jednym z trzech formatów:

  • TEXT – data jest zapisana w formacie ISO8601, czyli „YYYY-MM-DD HH:MM:SS.SSS”
  • REAL – data jest zapisana jako liczba dni, które mineły od 24 listopada 4714 r. p.n.e., czyli tak zwana data Juliańska
  • INTEGER – czas UNIXowy, czyli liczba milisekund, które mineły od 1 stycznia 1970r.

Do operacji na datach SqLite dostarcza 5 metod:

  1. date(timestring, …)
  2. time(timestring, …)
  3. datetime(timestring, …)
  4. julianday(timestring, …)
  5. strftime(format, timestring, …)

Pierwsze 4 funkcje przyjmują string jako pierwszy argument, a następnie dodatkowe argumenty. Funckja strftime przyjmuje jako pierwszy argument format daty, następnie wartość daty jako string oraz dodatkowe argumenty jako kolejne argumenty.

String opisujący date może być w jednym z poniższych formatów:

  1. YYYY-MM-DD
  2. YYYY-MM-DD HH:MM
  3. YYYY-MM-DD HH:MM:SS
  4. YYYY-MM-DD HH:MM:SS.SSS
  5. YYYY-MM-DDTHH:MM
  6. YYYY-MM-DDTHH:MM:SS
  7. YYYY-MM-DDTHH:MM:SS.SSS
  8. HH:MM
  9. HH:MM:SS
  10. HH:MM:SS.SSS
  11. now
  12. DDDDDDDDDD

Formaty od 1 do 4 wydają się być oczywiste. W formatach 5-7 występuję stały znak T, który służy jako separator daty i godziny. Formaty 8-10 służą do przechowywania samej godziny. Format 11 to string now, który odpowiada aktualnej dacie. Ostatni format jest to liczba zmiennoprzecinkowa opisująca date Juliańską.

OrmLite – realizacja relacji wiele-do-wielu

W tym wpisie przedstawiłem orientacyjny schemat bazy danych, który będzie zastosowany w mojej aplikacji. Można tam zauważyć relacje wiele-do-wielu pomiędzy tabelami tags i documents oraz tabele documents_tags, która jest tak zwaną tabelą skrzyżowań.

OrmLite niestety nie wspiera relacji wiele-do-wielu, ale sama realizacja tego na szczęście nie jest trudna.

Zacznę od pokazania encji dla tabeli documents_tags.

Jak widać encja zawiera tylko 3 pola:

  • id – id encji, które jest wymagane
  • document – klucz obcy odnoszący się do dokumentu
  • tag – klucz obcy odnoszący się do tagu

Encja tag wygląda następująco:

Wydaję mi się, że tutaj nie ma nic do tłumaczenia 🙂

Encja dla dokumentu wygląda natomiast tak:

Tutaj istotne jest następujące pole:

Jak widać jest to ‚obca’ kolekcja. Co będzie zawierała kolekcja po pobraniu dokumentu przy pomocy metody queryForId?

Otóż, jak można się domyślić, kolekcja ta będzie zawieraja liste encji DocumentTag powiązanych z dokumentem. Jest tylko jeden haczyk – pobrane encje DocumentTag nie zawierają ‚całych’ obiektów, a jedynie ich id. Czyli np. instancja klasy Tag w pobranej kolekcji bedzie zawierała uzupełnione id, lecz pole name zawsze będzie puste.

Mając tą wiedze wystarczą dwa kroki do pobrania naszych tagów.

Po pierwsze musimy stworzyć metodę, która na podstawie listy id tagów, zwróci nam tagi.

A mając pobraną naszą encje możemy pobrać jej taki wywołując powyższą metodę w następujący sposób:

 

Jak widać pomimo braku jawnego wsparcia dla relacji wiele-do-wielu możemy sobie z tym w miarę ładny sposób poradzić 🙂

Tworzenie layoutu w Androidzie

Jedną z rzeczy, która mnie bardzo pozytywnie zaskoczyła w pisaniu aplikacji na Androida jest sposób tworzenia layoutu. Chodzi mianowicie o separacje pomiędzy logiką aplikacji, a wartwą prezentacji, która jest opisywana w plikach XML.

Stworzenie aktywności z menu rozwijanym z boku może wyglądać następująco.

activity_main.xml

app_bar_main.xml

content_main.xml

 

Wydaję mi się, że tworzenie elementów layoutu jest na tyle intuicyjne, że nie będę szczegółowo opisywał co się dokładnie dzieje w powyższych listingach.

Jest jednak jednak jedna rzecz, na którą chciałbym zwrócić uwagę. Wewnątrz activity_main.xml umieszczany jest app_bar_main.xml, wewnątrz którego umieszczony jest content_main.xml.

Pierwsze dwa pliki są odpowiedzialne za tworzenie aktywności z bocznym menu, natomiast trzeci plik – content_main.xml – zawiera layout specyficzny dla konkretnego ekranu.

I teraz pojawia się problem, którego nie udało mi się rozwiązać w elegancki sposób.

Tworzę drugą aktywność, która ma zawierać to samo menu, lecz sam ekran ma mieć inną zawartość. I teraz, żeby to zrobić muszę skopiować dwa pierwsze layouty odpowiedzialne za menu, zmienić nazwy plików i wartości atrybutów w samych plikach. Podejście to oczywiście działa, lecz generuje sporo plików, które są praktycznie identyczne.

Rzeczą, która by się tutaj mogła przydać byłaby możliwość podania parametrów, które były by przekazywane do załączanego layoutu. Mam na myśli rozwiązanie podobne do tego, które jest dostępne np. w JSF.

Niestety, póki co nie udało mi się znaleźć rozwiązania tego problemu i w związku z tym, póki co zostaje z moim rozwiązaniem.

Ciekawy problem z ORMLite

Podczas implementacji ekranu edycji dokumentu natknąłem się na ciekawy błąd związany z ORM.

Podczas próby aktualizacji encji

otrzymałem poniższy błąd:

Błąd sugeruje, że encja ta nie posiada pola id. Sama encja wyglądała tak:

Encja oczywiście zawierała pole id, a sam zapis encji do bazy danych odbył się bez problemu.

Zanim przejdę do mojego rozwiązania to chciałbym odnieść się do znaku _, który występował przy kazdym polu, np:

Idąc za dokumentacją języka:

Czyli w przypadku pola id powinno do niego zostać przypisane domyślnie 0.

Wracając do mojego rozwiązania problemu. Jedyną zmianą, którą zrobiłem w moim kodzie to poniższa modyfikacja.

Jak widać bezpośrednie przypisanie 0 do pola id pomogło z rozwiązaniem problemu.

Robienie zdjęc w aplikacji

Jedną z funkcjonalności mojej aplikacji jest możliwość dodawania zdjęc do dokumentów. Samo robienie zdjęć jest dosyć proste.

Na początku musimy ustalić miejsce gdzie zdjęcie zostanie zapisane. Robię to w poniższy sposób:

Na początku tworzę folder „.temp”, do którego zostanie zapisane zrobione zdjęcie. Następnie tworzymy obiekt File, wskazujący dokladnie na plik, do którego ma nastąpić zapis.

Następnym krokiem jest stworzenie obiektu Uri, który będzie wskazywał na wcześniej stworzony plik:

Wywołanie samej akcji odpowiedzialnej za robienie zdjęcia wygląda następująco:

gdzie

jest stałą definiującą kod tej akcji, a

jest polem klasy i będzie przechowywać ścieżkę do zrobionego zdjęcia.

 

Po zrobieniu zdjęcia i zatwierdzeniu go wywoływana jest poniższa metoda:

Po sprawdzeniu czy requestCode jest zgodny z kodem naszej akcji oraz operacja jest zakończona sukcesem to wtedy możemy zająć się przetwarzaniem wykonanego zdjęcia.

Android file chooser dialog

API Androida niestety nie dostarcza nam w standardzie żadnego mechanizmu umożliwiającego wybór pliku z systemu plików. W takim przypadku nie pozostaje nam nic innego jak zaimplementować to samemu lub skorzystać z gotowej biblioteki.

Postanowiłem bazować na file chooser’ze zaimplementowanym przez Rogera Keaysa. Jest on dostępny pod poniższym adresem: https://rogerkeays.com/simple-android-file-chooser.

Moja implementacja wygląda w następujący sposób:

Główną zmianą (oczywiście poza napisaniem tego w języku Scala) jest dodanie ograniczenia na maksymalną wielkość pliku. Po wyborze pliku, jeżeli jego rozmiar przekracza ustawiony maksymalny rozmiar to zostaje wyświetlony komunkat z informacją, a plik nie zostaje dodany.