Qt no Android: como damos uma segunda vida a um aplicativo de meditação de autor

Uma breve introdução lírica - em 2017, eu estava muito interessado em meditações. Isso foi facilitado por toda uma cadeia de eventos, favoráveis ​​e não muito. Há muitos anos que me interesso e pratico sonhos lúcidos, mas antes disso não precisava lidar especificamente com meditações em sua forma canônica. Hoje em dia, muitosas histórias começam no (s) bar (s)Os passatempos começam com uma pesquisa no Google, então comecei assim. Quase imediatamente, foram encontradas as aplicações de meditação mais populares - Calm and Headspace.


O primeiro serviu como um bom ponto de partida (excelentes meditações educacionais para iniciantes), o segundo não me foi útil, não gostei da apresentação. Ambos se afastaram com seus planos tarifários pagos (e devo dizer muito caros para a Federação Russa). Talvez eu simplesmente não pertenço à categoria de pessoas que precisam pagar para se encorajarem a fazer algo :) Continuando a estudar o Google Play, me deparei com dois aplicativos gratuitos que estão perto de mim em espírito. O primeiro é "Vamos meditar" - eu ainda o uso, o segundo será discutido no corpo do artigo.


inscrição


Então, depois de uma longa pesquisa, foi encontrada uma aplicação completamente discreta e foi chamada, se não me engano, "Meditação. Antonov Alexander". Como se viu, foi possível ouvir quatro meditações do autor, gravadas e compostas, de fato, por Alexander, a quem mais tarde conhecemos e fizemos amizade. Ele montou o aplicativo literalmente a partir de ferramentas improvisadas, era algo como um SPA caseiro usando o WebView sem nenhuma estrutura, praticamente em HTML simples e minimamente em Java. Parecia mais ou menos, e algumas funções estavam simplesmente ausentes (por exemplo, você não podia navegar pela gravação, mas apenas ativá-la desde o início). Como eu realmente gostei do conteúdo em si, ofereci a Alexander minha ajuda gratuita na atualização do aplicativo, de modo que, por assim dizer, "para devolver algo "com base em" eles me ajudaram, eu ajudarei. "No corpo do artigo, tentarei dizer quais problemas encontramos durante o desenvolvimento, que decisões foram tomadas e o que aconteceu no final! Espero que as receitas individuais do artigo sejam útil para qualquer pessoa, mas interessante:)



, :


  • 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 .

Em geral, desenvolver Qt é divertido, fazer algo de graça e também para a alma!


PS Prezados residentes de Khabrovsk, espero que você não conte como publicidade - o aplicativo é fundamentalmente gratuito e não comercial, eu queria contar sobre isso e o histórico de sua criação para aqueles que estavam interessados, apenas para compartilhar abordagens de desenvolvimento encontradas com sucesso.
PSS O aplicativo possui sua versão gêmea 1.1 com entradas mais antigas, com mais 5k de downloads e 100 avaliações. Provavelmente sairemos da loja em breve.



All Articles