Qt di Android: bagaimana kami memberikan kehidupan kedua ke aplikasi meditasi penulis

Pengantar liris singkat - pada 2017, saya sangat tertarik pada meditasi. Ini difasilitasi oleh seluruh rangkaian acara, menguntungkan dan tidak terlalu. Selama bertahun-tahun saya telah tertarik dan mempraktikkan mimpi jernih, tetapi sebelum itu saya tidak harus berurusan secara khusus dengan meditasi dalam bentuk kanonik mereka. Saat ini, banyakcerita dimulai di barHobi dimulai dengan pencarian di Google, jadi saya memulainya dengan cara itu. Hampir segera, aplikasi meditasi paling populer ditemukan - Tenang dan Headspace.


Yang pertama berfungsi sebagai titik awal yang baik (meditasi pendidikan yang sangat baik untuk pemula), yang kedua saya merasa tidak berguna untuk diri saya sendiri, saya tidak suka presentasi. Keduanya terdorong pergi dengan rencana tarif berbayar mereka (dan saya harus mengatakan sangat mahal untuk Federasi Rusia). Mungkin saya hanya tidak termasuk dalam kategori orang yang perlu membayar untuk mendorong diri mereka untuk melakukan sesuatu :) Terus belajar Google play, saya menemukan dua aplikasi gratis yang dekat dengan saya dalam semangat. Yang pertama adalah "Ayo Bermeditasi" - Saya masih menggunakannya, yang kedua akan dibahas dalam tubuh artikel.


aplikasi


Jadi, setelah pencarian yang cukup lama, aplikasi yang sama sekali tidak mencolok ditemukan, kemudian dipanggil, jika saya tidak salah, "Meditasi. Antonov Alexander." Ternyata, adalah mungkin untuk mendengarkan meditasi empat penulis di dalamnya, direkam dan disusun, pada kenyataannya, oleh Alexander, yang kemudian kita temui dan berteman. Dia merakit aplikasi secara harfiah dari alat improvisasi sendiri, itu seperti SPA buatan sendiri menggunakan WebView tanpa kerangka kerja, praktis dalam HTML telanjang dan minimal di Jawa. Itu terlihat begitu-begitu, dan beberapa fungsi tidak ada (misalnya, Anda tidak dapat menavigasi melalui rekaman, tetapi hanya menyalakannya dari awal). Karena saya sangat menyukai kontennya, saya menawarkan Alexander bantuan gratis saya untuk meningkatkan aplikasi, jadi, untuk berbicara, "untuk memberikan sesuatu kembali "atas dasar" mereka membantu saya, saya akan membantu. "Dalam tubuh artikel saya akan mencoba untuk memberi tahu Anda apa masalah yang kami temui selama pengembangan, keputusan apa yang dibuat, dan apa yang terjadi pada akhirnya! Saya berharap bahwa masing-masing resep artikel akan menjadi berguna untuk siapa saja, tetapi menarik :)



, :


  • UI UX

Qt


β€” (), PHP/HTML. , , , Qt, :


  • Qt ( Symbian, MeeGo, Ubuntu Phone Android);
  • , ;
  • QML, C++. , β€” JavaScript-like , ;
  • iOS ( ).

, β€” , .


UI


, , . , , . , . ( ):



"" anchors, Row, Column Repeater. , UI. , ( ):


Button {
id: mainButton
anchors {
    left: parent.left
    right: parent.right
}
height: btnLayout.height + 30
Material.background: "white"
onClicked: stackView.push(Qt.resolvedUrl("qrc:/qml/MeditationListPage.qml"))

Column {
    id: btnLayout
    spacing: 10
    anchors {
        top: parent.top
        topMargin: 15
        left: parent.left
        right: parent.right
        margins: 10
    }

    Row {
        anchors.horizontalCenter: parent.horizontalCenter
        spacing: (mainButton.width - 4 * 50) / 6
        Repeater {
            model: meditationModel

            RoundedIcon {
                source: Qt.resolvedUrl("qrc:/img/my%1.png".arg(model.index))
                color: model.color
                width: 50
                height: 50
            }
        }
    }

    Label {
        text: qsTr("")
        font.pointSize: 14
        color: "dimgrey"
        anchors.horizontalCenter: parent.horizontalCenter
    }

    Label {
        text: "        ,     "
        anchors {
            left: parent.left
            right: parent.right
        }
        horizontalAlignment: Text.AlignHCenter
        Material.foreground: Material.Grey
        font.pixelSize: 12
        wrapMode: Text.WrapAtWordBoundaryOrAnywhere
    }
}
}

UI , HDPI Qt . , Qt: QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);. " ".


Material design, Android Qt Quick Controls 2 . https://doc.qt.io/qt-5/qtquickcontrols2-material.html. , Material design. UI , attached property Material.accent .




mp3- , QRC. 10-15 . β€” , 15 . , , pro-:


CONFIG += resources_big

, , , .



, " ". Shorts, , . DarkModeShader.qml:


ShaderEffect {
    fragmentShader: "
        uniform lowp sampler2D source;
        uniform lowp float qt_Opacity;
        varying highp vec2 qt_TexCoord0;

        void main() {
            lowp vec4 p = texture2D(source, qt_TexCoord0);
            p.r = min(0.8, (1.0 - p.r) * 0.8 + 0.1);
            p.g = min(0.8, (1.0 - p.g) * 0.8 + 0.1);
            p.b = min(0.8, (1.0 - p.b) * 0.8 + 0.1);
            gl_FragColor = vec4(vec3(dot(p.rgb, vec3(0.299, 0.587, 0.114))), p.a) * qt_Opacity;
        }
    "
}

:


StackView {
    // ...
    layer.effect: DarkModeShader { }
    layer.enabled: optionsKeeper.isNightMode
}

.. , isNightMode. Qt / ( , ).


, β€” . - , β€” !



Qt Multimedia, Audio. mp3, , β€” , :


Audio {
    id: audioPlayback
    source: meditAudioSource
}
// ...
Slider {
    anchors {
        left: parent.left
        right: playBtn.left
        verticalCenter: parent.verticalCenter
    }
    from: 0
    to: audioPlayback.duration
    value: audioPlayback.position
    onMoved: audioPlayback.seek(value)
    Material.accent: meditColor
}

Settings Qt.labs.settings (, , labs):


import Qt.labs.settings 1.0
// ...  boilerplate- ,        .
property Settings settings: Settings {
    property bool isNightMode: false
}

2018 , .



, , 2020, , - , β€” ( ). , β€” β€” :)



, JSON- . . HTTP- QML- XMLHttpRequest ( JSON QML). .



. QML LocalStorage, SQLite. JS-, QML, :


// databasemodule.js
.pragma library // I hope this will prevent the waste of memory.
.import QtQuick.LocalStorage 2.0 as SQL

function getMeditations() {
    ...
}

// TransferManager.qml
import "databasemodule.js" as DB
...
var syncedItems = DB.getMeditations()

:


var db = SQL.LocalStorage.openDatabaseSync("AMeditation", "", "Main DB", 100000)
...
db.transaction(function(tx) {
    dbResult = tx.executeSql("SELECT * FROM meditations")
    console.log("meditations SELECTED: ", dbResult.rows.length)
})

.. , transaction callback. ( , Qt ).
. openDatabaseSync . , ( , ). . , , , . [" ", " ", " "]:


var migrations = [
    {'from': "", 'to': "1.0", 'ops': function(transaction) {
        transaction.executeSql("CREATE TABLE meditations ( \
            id  INTEGER PRIMARY KEY 
            ...
            status  TEXT);")
    }}
    ,{'from': "1.0", 'to': "1.1", 'ops': function(transaction) {
        transaction.executeSql("ALTER TABLE meditations ADD quality TEXT;")
    }}
]

, ( β€” , - ).


C++


QML, C++. . Ubuntu Phone. . QML β€” . C++- QML :


engine.rootContext()->setContextProperty("networkManager", new NetworkManager());

QML - :


//  .
var isSucces = networkManager.download(downloadUrl, currentDownload.localUrl)
...
//  .
Connections {
    target: networkManager

    onDownloadOperationProgress: {
        d.currentDownload.current = current
        d.currentDownload.total = total
    }
    ...
}

C++-, QML- singletone :


// cpputils.h
class CppUtils : public QObject
{
    Q_OBJECT
public:
    explicit CppUtils(QObject *parent = nullptr);
    ~CppUtils();

    Q_INVOKABLE bool removeFile(const QString& fileName) const;
    static QObject *cppUtilsSingletoneProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
};

// main.cpp
qmlRegisterSingletonType<CppUtils>("AMeditation.CppUtils", 1, 0, "CppUtils", CppUtils::cppUtilsSingletoneProvider);

.. , cppUtilsSingletoneProvider, Q_INVOKABLE β€” "" QML. QML :


import AMeditation.CppUtils 1.0
// ...
CppUtils.removeFile(cd.localUrl)


, - . β€” . , - , . , . QML (QNetworkAccessManager). . :


// cachingnetworkmanagerfactory.h
class CachingNetworkAccessManager : public QNetworkAccessManager
{
public:
    CachingNetworkAccessManager(QObject *parent = 0);
protected:
    QNetworkReply* createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData = 0);
};

class CachingNetworkManagerFactory : public QQmlNetworkAccessManagerFactory
{
public:
    CachingNetworkManagerFactory();
    QNetworkAccessManager *create(QObject *parent);
};

// cachingnetworkmanagerfactory.cpp

QNetworkReply* CachingNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
{
    QNetworkRequest req(request);
    req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork);
    return QNetworkAccessManager::createRequest(op, req, outgoingData);
}

QNetworkAccessManager *CachingNetworkManagerFactory::create(QObject *parent) {
    QNetworkAccessManager* manager = new CachingNetworkAccessManager(parent);

    QNetworkDiskCache* cache = new QNetworkDiskCache(manager);
    cache->setCacheDirectory(QString("%1/network").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
    manager->setCache(cache);
    return manager;
}

.. setCache , createRequest . β€” , .


Android


SDK NDK. Linux- , Windows - (, , QtCreator 4.12 NDK, ). NDK Clang. arm_v7 (32 ), arm_v8a (64 ; Google play 2019 ). Google play.


, , .



  • Qt pet-, Android. ;
  • AMeditation ( UI " 2.1. ") 10+ 250 , ( , , ).
  • , , !
  • iOS .

Secara umum, mengembangkan Qt itu menyenangkan, melakukan sesuatu secara gratis dan untuk jiwa juga!


PS Warga Khabrovsk yang terhormat, saya harap Anda tidak menganggapnya sebagai iklan - aplikasi ini pada dasarnya gratis dan non-komersial, saya ingin menceritakan tentang hal itu dan sejarah pembuatannya kepada mereka yang tertarik, hanya ingin berbagi pendekatan pembangunan yang berhasil ditemukan.
PSS Aplikasi ini memiliki versi kembarannya 1.1 dengan entri yang lebih lama, yang memiliki unduhan 5k + lainnya dan 100 ulasan. Kami mungkin akan segera mengeluarkannya dari toko.



All Articles