OptionsDialog.

TreeView na Androidzie sprawdza się średnio, zastanawiałem się jak to można by zrobić w Qt5 i przyznam szczerze, łatwo nie jest. QScrollView ma jedną podstawową wadę: póki co nie obsługuje przewijania jednym palcem i trzeba mu zostawić włączony scrollbar, na szczęście udało się go poprawić za pomocą stylów:

O kwestię layoutów mam ochotę zapytać któregoś z developerów, bo niestety są bardzo nieprzewidywalne. Nigdy nie wiadomo gdzie i jak dany widget się wyświetli. Na powyższym zrzucie odległości między obiektami są różne, a powinny być względnie spójne, bo na samym dole kontenera dodałem expander, który powinien wszystko co powyżej niego ładnie do siebie dosunąć…

Mam nadzieję, że kiedyś pojawi się spójne scrollview z obsługą single finger pan. Póki co się nie zanosi. A przecież aplikacji w QtWidgets jest niewspółmiernie więcej na wolności, niż w QtQuick.

AspeQt na Kazam Tornado 348.

A oto żywy dowód na prawidłową obsługę usb-host przez Kazam Tornado:

Przy okazji powiększyłem trochę przyciski i jeśli ekran jest mniejszy od 5″ to log jest wyłączony (wyświetla się na pasku statusu), a hbox-y wypełniają cały ekran:

Screenshot_2015-09-06-02-59-05Z małych ważnych zmian: usunąłem orientację poziomą i dzięki temu można sobie wybrać plik do załadowania w Fileselektorze w pionie (więcej się mieści).

scr-orient

 

Pełny USB-Host na Kazam Tornado 348.

Nie doczekałem się na rozwiązanie problemu przez support Kazam, więc postanowiłem dać radę sam ;).

usb-host-kazamPostanowiłem zrobić to najprościej jak się da, bez wgrywania nieoficjalnych ROM-ów itp. Niestety trzeba telefon zrootować. na szczęście z użyciem Kingo Root jest to łatwe.

Przełączamy telefon do trybu przechowywania plików (dotknij aby skopiować pliki w powiadomieniu, po podłączeniu do laptopa)  i tworzymy w katalogu głównym karty pamięci telefonu plik: android.hardware.usb.host.xml o zawartości:

<permissions>
    <feature name=”android.hardware.usb.host” />
</permissions>

Odłączamy kabel USB.

Podłączamy go ponownie, tym razem już bez klikania w powiadomienie, tylko w trybie debugowania USB i łączymy się z telefonem:

adb shell
su
mount -o remount,rw /system
cp /sdcard/android.hardware.usb.host.xml /etc/permissions/
chmod 777 /etc/permissions/android.hardware.usb.host.xml
mount -o remount,ro /system

I po ponownym uruchomieniu usb-host działa (obie przelotki RS232-USB działają prawidłowo).

Bez powyższego da się np. podłączyć pendrive i skopiować pliki, ale żadnych innych bardziej skomplikowanych urządzeń.

Teraz spokojnie mogę zająć się debugowaniem AspeQt na KitKat i dalszą optymalizacją gui.

AspeQt dla Windows.

Piętnaście emulowanych napędów w AspeQt to było dla mnie zdecydowanie za dużo 🙂 więc „zandroidyzowałem” plik projektu z repo dla Androida i wydzieliłem co androidowe, dzięki czemu mój fork AspeQt dla Antka kompiluje się też pod Windows i pod Linuksem. Obsługa portu odbywa się wtedy przez standardowy moduł serialport-{win32,linux}.cpp. Przy okazji poprawiłem kodowanie znaków, które w AspeQt pod Windows było skopane od dawna:

AspeQt_WindowsBinarka dla Windows z wymaganymi bibliotekami tutaj.

winandroidlinux

Qt5 – Natywny file&directory selector przez JNI.

QFileDialog w Qt5 na Androidzie jest fatalny. Na szczęście znalazłem minimalistyczny fileselector:

http://www.scorchworks.com/Blog/simple-file-dialog-for-android-applications/

który świetnie się sprawdza w AspeQT:

IMG_20150719_123904Musiałem go tylko lekko zmodyfikować, bo na nazwę pliku oczekuję w pętli po stronie Qt (lame, ale brakło mi czasu na bardziej eleganckie rozwiązanie)  i bez Cancel nie dało się z niej wyskoczyć:

QAndroidJniObject::callStaticMethod<void>("net/greblus/MyActivity", "runFileChooser", "()V");

QString fileName = NULL;
do
{
   QAndroidJniObject jFileName = QAndroidJniObject::getStaticObjectField<jstring>("net/greblus/MyActivity", "m_chosen");
   fileName = jFileName.toString();

   if (fileName == "Cancelled") {fileName.clear(); break;}
   if (fileName == "None") QThread::yieldCurrentThread();
}
while (fileName == "None");

A w Javie, cały widz polega na tym, że MyActivity jest singletonem i niestatyczne metody (jak w FileChooser) trzeba wołać w wątku UI Androida z instancji singletona:


public class MyActivity extends QtActivity
{
   ...
   public static MyActivity s_activity = null;
   ...
   public void onCreate(Bundle savedInstanceState)
   {
      s_activity = this;
   }
   ...
}
public static void runFileChooser() {
   m_chosen = "None";
   MyActivity.s_activity.runOnUiThread( new FileChooser() );
}

i wtedy można z C++ wywołać QAndroidJNIObject tak:

QAndroidJniObject::callStaticMethod<void>("net/greblus/MyActivity", "runFileChooser", "()V");

Lekko zmodyfikowany plik tutaj.

bigger_aspeqt

AspeQt na Androidzie.

Update: AspeQT na Androida jest dostępny w Google Play.

Jak niektórzy czytelnicy atarowych forów pewnie zauważyli, udało się. AspeQt działa na moich „Antkach” (4.2.2 Jelly Bean i 4.4.2 Kitkat).

Nie obyło się bez przygód. Z obsługą Kitkata miałem problem wynikający z dziwnego podejścia producentów lub ich nieświadomości: dostałem ostatnio od operatora jako nowy klient, bardzo fajny telefon: Kazam Tornado 348. Pod względem jakość/cena, polecam każdemu. Okazało się jednak, że tenże Kazam obsługuje OTG, czyli mogę sobie podłączyć np. pendrive, ale już USB Host na nim nie działa jak trzeba (pewnie jest nieskonfigurowany, a ja go rootować nie mam zamiaru). I cały czas, o ja głupi, myślałem, że to wina sterownika ftd2xx od FTDI, lub też zmian w KitKacie związanych z obsługą tzw. BroadcastReceiverów.

Jak widać powyżej na zupełnie budżetowym Kazam 345, wszystko śmiga aż miło (swoją drogą na prawdę fajny telefon, ale jakościowo Tornado vs Thunder dzieli ogromna przepaść). Support Kazam obiecał zająć się tematem. Może spodobało im się Atari…

Piszę to dlatego, że użytkownicy powinni się buntować. Dlaczego jeżeli sprzętowo coś jest dostępne, celowo pozbawiać użytkownika możliwości korzystania z tego? Bo co? Bo podłączy sobie przez USB klawiaturę/głośniki/kamerę dowolnego producenta? Paradoksalnie, tanie produkty, jak np. mój Lark FreeMe X2 nie mają z tym problemu. Ale już np. Samsung S5 mini, którego mam z pracy USB nie obsługuje wcale. To nic, że kosztuje krocie…

Przed AspeQT na Androidzie jeszcze daleka droga:

  • Muszę poprawić GUI, żeby dało się go obsłużyć wygodnie i na małym i na dużym ekranie.
  • Okno wyboru pliku i katalogu to w Qt5 na Androida jakaś masakra. Trzeba będzie poeksperymentować z dostępnymi natywnymi bibliotekami, które tą funkcjonalność oferują i obsłużyć to przez JNI.
  • Kontrola przepływu i prędkość transmisji, tego póki co się najbardziej obawiam. Póki co ustawiam FT_FLOW_NONE i prędkość na 19200. DSR nie chce działać, a po ustawieniu prędkości powyżej 19200 bez kontroli przepływu skutkuje odczytem bzdurnych danych.

Ale nic na siłę, wszystko w swoim czasie ;).

Dla zainteresowanych repo na github:

https://github.com/greblus/aspeqt

Apka poniżej (pod tym linkiem wrzucam najnowsze wersje bez uprzedzenia ale w katalogu android/apk/ zachowuje poprzednie, z datą kompilacji):

https://github.com/greblus/aspeqt/blob/android/android/apk/aspeqt.apk

Java ByteBuffer w Qt5.

1. Współdzielenie danych między Javą a Qt5 chyba mam opanowane:

ByteArrayW Javie to wygląda np. tak:

public ByteBuffer bbuf = ByteBuffer.allocateDirect(10);
public byte b[] = new byte [] {1, 1, 2, 3, 5, 8, 13, 21, 34, 55};
public static native void sendBufAddr(ByteBuffer buf);
// natywna funkcja w C++ do której przekazujemy adres bufora
// dane z bufora pakujemy do ByteBuffera w ten sposób:
bbuf.put(b);
// i wysyłamy adres do funkcji w C++
sendBufAddr(bbuf);

A funkcja w C++ w wersji minimalistycznej w QT5 wygląda tak:

extern "C" {
   JNIEXPORT void JNICALL
   Java_net_greblus_MyActivity_sendBufAddr(JNIEnv *env,
   jobject, jobject buf)
   {
   jbyte *bbuf = (jbyte *)env->GetDirectBufferAddress(buf);
   // i możemy robić z danymi w bbuf[] co chcemy
   }
}

2. Pakiet jar ze sterownikiem do FTDI działa w Qt5 w MainActivity, więc wszystkie elementy układanki zaczynają do siebie pasować ;).

D2XX + Sio2PC-USB.

Niestety chyba na chwilę odpuszczę próby odpalenia Sio2PC-USB na Androidzie bez roota z opensource-owego libftdi. Nauczyłem się obsługiwać JNI w QT5, wszystko niby działa, ale moje zmodyfikowane libusb coś niedomaga. libftdi zwraca error -4 przy próbie otwarcia pliku z wykorzystaniem wydłubanego w Javie (po nadaniu uprawnień) deskrypytora pliku… Może to być wina konfiguracji Antka, a tego bez modyfikacji obrazu systemu nie przeskoczę. Muszę spróbować czegoś innego:20150605_011113Wracam do sterownika D2XX od FTDI. Ten działa z poziomu Javy na Androidzie zawijając ftdi i libusb w JNI w ładny pakiet Javowy (więc będziemy to zawijać dwa razy ;)), ale przynajmniej da się w nim otworzyć urządzenie. To był mój pierwszy pomysł jak to zrobić: Wywołać API Javowe sterownika przez JNI, wszystko z urządzeniem robić w MainActivity w Javie, a dane współdzielić z QT przez ByteBuffer. Teraz już wiem, że jest to do zrobienia i nie jest to trudne.