Qt Quick & Android: Welcome to the real world 

Riccardo Ferrazzo

rferrazz@develer.com

Matteo Baracani

bara@develer.com

Chi siamo

Il progetto

Perché Qt?

  • "Facilmente" portabile
  • Possibilità di riutilizzare librerie di applicazioni desktop
  • Non utilizzare "troppo" linguaggi nativi della piattaforma

Argomenti del talk

  • Configurazione dell'ambiente
  • Gestione risorse dell'applicazione
  • Creazione di interfacce scalabili e interfacce con look&feel nativo
  • Gestione input da tastiera (tastiera virtuale android)
  • Uso di librerie di terze parti
  • Chiamata di funzioni Java da codice C++
  • Implementazione di funzioni Java native in C++
  • Creazione di oggetti Java in C++

Configurazione dell'ambiente

Cosa serve

  • Qt >= 5.4 
  • Android SDK Tools o Android Studio
    • Android SDK platform >= 15
    • Intel x86 Emulator Accelerator
  • Android NDK
  • JDK v6 o successiva

Configurare Qt Creator

Gestione risorse

The golden rule

Utilizzare il sistema di risorse Qt

Come utilizzare il file qrc

  • Suddividere in sottocartelle
  • Utilizzare alias e prefix
  • Mettere file di piccole dimensioni

Creazione dell'interfaccia

Ottenere un look&feel nativo

  • Qt Quick components
  • Librerie di terze parti
  • Creazione di componenti custom

Testo

Qt fornisce un'unità di misura scalabile, font.pointSize, che però non tiene conto della densità di pixel del display.

Componenti

Non esiste un'unità di misura scalabile. 

Scalabilità (problemi)

Immagini

Android associa risorse diverse in base alla risoluzione dello schermo

Con Qt possiamo utilizzare SVG

Scalabilità (soluzione)

namespace {
    const double REFERENCE_DPI = 150;
}

Units::Units(QObject *parent) :
    QObject(parent)
{
    // due to QTBUG-35701 we cannot use phisycaldotsperinch * devicepixelratio on android
#ifdef Q_OS_ANDROID
    QAndroidJniObject qtActivity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", 
        "activity", "()Landroid/app/Activity;");
    QAndroidJniObject resources = qtActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;");
    QAndroidJniObject displayMetrics = resources.callObjectMethod("getDisplayMetrics",
        "()Landroid/util/DisplayMetrics;");
    int dpi = displayMetrics.getField<int>("densityDpi");
#else
    auto screen = qApp->primaryScreen();
    int dpi = screen->logicalDotsPerInch() * qApp->devicePixelRatio();
#endif

    scale_ratio = dpi / REFERENCE_DPI;
}

int Units::dp(int pixels)
{
    return ceil(pixels * scale_ratio);
}

Input da tastiera

Qt.inputMethod

Oggetto globale di Qt che fornisce informazioni sul sistema di input

Tipi diversi di tastiera

Possibilità di visualizzare tastiere diverse in base tipo di input richiesto

Uso di librerie esterne

L'uso di Qt permette di utilizzare librerie C++ e/o librerie Java

Integrazione

Java - C++

Cosa serve

  • Nel file di .pro  alla configurazione di Qt aggiungere il modulo androidextras
  • Includere   <QAndroidJniObject>
  • Includere   <QAndroidJniEnvironment>

JNI: framework che permette l'interoperabilità Java - codice nativo

QAndroidJniObject: fornisce le API per chiamare codice Java da C++

QAndroidJniEnvironment: fornisce l'accesso al JNI Environment

Chiamare una funzione Java da C++ (1)


static_cast<bool>(
  QAndroidJniObject::callStaticMethod<jboolean>(
    "org/qt/MyActivity", 
    "checkUSBPermission"));

jboolean call = call_load_file_on_confirm ? \
  JNI_TRUE : JNI_FALSE;
QAndroidJniObject::callStaticMethod<void>(
  "org/qt/MyActivity",
  "askDevicePermission", "(Z)V", call);

QAndroidJniObject::callStaticMethod<void>(
  "org/qt/MyActivity", "print",
  "(II)V", 1, 2);

Java

C++

public class MyActivity extends QtActivity {
  public static boolean checkUSBPermission() {
    ...
  }


  public static void askDevicePermission(
    boolean call_load) {
    ...
  }


  public static void print(int type,
    int index) {
    ...
  }

}

Chiamare una funzione Java da C++ (2)


QAndroidJniObject::callStaticMethod<void>("org/qt/MyActivity", "printString", 
  "(ILjava/lang/String;)V", 1, QAndroidJniObject::fromString("try to print").object<jstring>());


QAndroidJniEnvironment env;
QAndroidJniObject object_file_content = QAndroidJniObject::callStaticObjectMethod<jbyteArray>(
  "org/qt/MyActivity", "loadDataFromFile");
jbyteArray file_content = object_file_content.object<jbyteArray>();
jsize size = env->GetArrayLength(file_content);
if (size) {
  jbyte *byte_content = env->GetByteArrayElements(file_content, JNI_FALSE);
  QByteArray content((const char*)byte_content, size);
}
public class MyActivity extends QtActivity {
  
  public static void printString(int type, String string_to_print) { ... }

  public static byte[] loadDataFromFile() { ... }
}

Creazione oggetti Java in C++

public class PrintInformation {
  ...
  public PrintInformation(String value) {
    ...
  }
}

public class MyActivity extends QtActivity {
  
  public static void setColorInfo(PrintInformation print_information) {
    ...
  }
}
auto str = QAndroidJniObject::fromString(QString("hello"));
QAndroidJniObject value("org/qt/PrintInformation", "(Ljava/lang/String;)V",
  str.object<jstring>());
QAndroidJniObject::callStaticMethod<void>("org/qt/MyActivity",
  "setColorInfo", "(Lorg/qt/PrintInformation;)V", value.object());

Implementazione funzioni Java native in C++ (1)

public class MyActivity extends QtActivity {
  private static native void printerFound(String name, String mac, int type, int index);
}
class ConfigurationManager {

  static void printerFound(JNIEnv *env, jobject thiz, jstring name, 
    jstring mac, jint printer_type, jint index) {
    ...
  }
}

Java:

C++:

Implementazione funzioni Java native in C++ (2)

static JNINativeMethod methods[] {{"printerFound",
  "(Ljava/lang/String;Ljava/lang/String;II)V",
  (void*)ConfigurationManager::printerFound}};

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{
    JNIEnv* env; // get the JNIEnv pointer.
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
        return JNI_ERR;
    // search for Java class which declares the native methods
    jclass javaClass = env->FindClass("org/qt/MyActivity");
    if (!javaClass)
        return JNI_ERR;

    // register our native methods
    if (env->RegisterNatives(javaClass, methods,
                             sizeof(methods) / sizeof(methods[0])) < 0) {
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

Fine

Domande?

Qt Quick

By Matteo Baracani

Qt Quick

  • 818