Commit b217f102 authored by Johannes Schwab's avatar Johannes Schwab

Merge branch 'release/0.2.1'

parents 2c0ea4e1 bc0ed48a
......@@ -2,7 +2,7 @@
<manifest package=\"org.jschwab.openrecipes$$NAMEPOSTFIX\"
xmlns:android=\"http://schemas.android.com/apk/res/android\"
android:versionName=\"$$VERSION\"
android:versionCode=\"2\"
android:versionCode=\"$$ANDROID_VERSION_CODE\"
android:installLocation=\"auto\">
<application android:hardwareAccelerated=\"true\"
android:name=\"org.qtproject.qt5.android.bindings.QtApplication\"
......@@ -48,6 +48,14 @@
<category android:name=\"android.intent.category.BROWSABLE\"/>
<data android:mimeType=\"openrecipes/recipe_xml\"/>
</intent-filter>
<intent-filter>
<action android:name=\"android.intent.action.VIEW\"/>
<category android:name=\"android.intent.category.DEFAULT\"/>
<category android:name=\"android.intent.category.BROWSABLE\"/>
<data android:scheme=\"content\"/>
<data android:host=\"*\"/>
<data android:mimeType=\"application/octet-stream\"/>
</intent-filter>
<!-- Application arguments -->
<!-- meta-data android:name=\"android.app.arguments\" android:value=\"arg1 arg2 arg3\"/ -->
......@@ -101,7 +109,7 @@
<!-- extract android style -->
</activity>
<provider android:authorities=\"org.jschwab.openrecipes.provider\"
<provider android:authorities=\"org.jschwab.openrecipes$${NAMEPOSTFIX}.provider\"
android:name=\"org.jschwab.openrecipes.XmlProvider\"
android:enabled=\"true\"
android:exported=\"true\"
......
......@@ -2,11 +2,11 @@ cd `dirname "${BASH_SOURCE[0]}"`
make distclean
export OPENRECIPES_ANDROID_SYSROOT=`pwd`/androidlibs/arm
export ANDROID_NDK_PLATFORM=android-27
~/Qt/5.11.1/android_armv7/bin/qmake
~/Qt/5.11.1/android_armv7/bin/qmake CONFIG+=debug
make clean
make -j5
mkdir client/android_arm
make install INSTALL_ROOT=android_arm
cd client
export TERM=xterm-color
~/Qt/5.11.1/android_armv7/bin/androiddeployqt --output android_arm --gradle --input android-libopenrecipes.so-deployment-settings.json --release
~/Qt/5.11.1/android_armv7/bin/androiddeployqt --output android_arm --gradle --input android-libopenrecipes.so-deployment-settings.json
......@@ -2,11 +2,11 @@ cd `dirname "${BASH_SOURCE[0]}"`
make distclean
export OPENRECIPES_ANDROID_SYSROOT=`pwd`/androidlibs/x86
export ANDROID_NDK_PLATFORM=android-27
~/Qt/5.11.1/android_x86/bin/qmake
~/Qt/5.11.1/android_x86/bin/qmake CONFIG+=debug
make clean
make -j5
mkdir client/android_x86
make install INSTALL_ROOT=android_x86
cd client
export TERM=xterm-color
~/Qt/5.11.1/android_x86/bin/androiddeployqt --output android_x86 --gradle --input android-libopenrecipes.so-deployment-settings.json --release
~/Qt/5.11.1/android_x86/bin/androiddeployqt --output android_x86 --gradle --input android-libopenrecipes.so-deployment-settings.json
cd `dirname "${BASH_SOURCE[0]}"`
make distclean
~/Qt/5.10.0/gcc_64/bin/qmake
~/Qt/5.10.0/gcc_64/bin/qmake CONFIG+=debug
make clean
make -j5
make check
......@@ -50,10 +50,9 @@ android {
ANDROID_EXTRA_LIBS += \
$$(OPENRECIPES_ANDROID_SYSROOT)/lib/libsodium.so \
$$(OPENRECIPES_ANDROID_SYSROOT)/lib/libqrencode.so
QMAKE_SUBSTITUTES += ../android-sources/AndroidManifest.xml.in
}
QMAKE_SUBSTITUTES += ../android-sources/AndroidManifest.xml.in
TRANSLATIONS += intl/client_de.ts
lupdate_only {SOURCES += qml/*.qml}
......
......@@ -69,4 +69,12 @@ ApplicationWindow {
push("qrc:/qml/SettingsView.qml");
}
}
Connections {
target: backend
onRecipeImported: {
mainRowLayout.selectRecipe(backend.getRecipe(id));
recipesListView.highlightRecipe(id);
}
}
}
......@@ -64,4 +64,9 @@ ApplicationWindow {
backend.stopSearching();
}
}
Connections {
target: backend
onRecipeImported: stackView.selectRecipe(backend.getRecipe(id))
}
}
......@@ -354,4 +354,8 @@ Page {
}
}
}
function highlightRecipe(id) {
recipesList.currentIndex = backend.getRecipeListIndex(id);
}
}
......@@ -19,18 +19,19 @@
#include "Backend.h"
#include "SqlBackend.h"
#include "Recipe.h"
//#include "RecipeTcpServer.h"
//#include "RecipeTcpClient.h"
#include "SynchronizeAsync.h"
#include "SendSyncKeyAsync.h"
#include "RecvSyncKeyAsync.h"
#include <QVariant>
#include <QLocale>
#include <cassert>
#ifdef Q_OS_ANDROID
#include "Jni.h"
#include <QAndroidIntent>
#include <QtAndroid>
#include <QAndroidJniEnvironment>
#endif
#include <cassert>
Backend::Backend()
:
......@@ -38,13 +39,16 @@ Backend::Backend()
sendSyncKeyAsync(nullptr),
recvSyncKeyAsync(nullptr),
recipes(SqlBackend::getRecipes())
#ifdef Q_OS_ANDROID
, androidResultReceiver(this)
#endif
{
for (auto &r : recipes) r->setParent(this);
sortRecipes();
SqlBackend::connectSignals(this);
#ifdef Q_OS_ANDROID
connect(Jni::getNotifier().get(), &JniNotifier::recipeImported, this, [this](const QByteArray &id) {
emit recipeImported(id);
});
#endif
}
QQmlListProperty<Recipe> Backend::getRecipesList() {
......@@ -60,11 +64,34 @@ QQmlListProperty<Recipe> Backend::getRecipesList() {
);
}
Recipe* Backend::getRecipe(const QByteArray &id) {
try {
return SqlBackend::getRecipe(id);
} catch (const InternalError &e) {
return nullptr;
} catch (const SqlNoResult &e) {
return nullptr;
}
//TODO Who frees the memory?
}
Recipe* Backend::getNewRecipe() {
return SqlBackend::addEmptyRecipe();
try {
return SqlBackend::addEmptyRecipe();
} catch (const InternalError &e) {
return nullptr;
}
//TODO Who frees the memory?
}
int Backend::getRecipeListIndex(const QByteArray &id) {
for (size_t i = 0; i < static_cast<size_t>(recipes.size()); ++i) {
if (recipes[i]->getId() == id) return i;
}
qWarning() << "Can't find id in recipes";
return 0;
}
bool Backend::deleteRecipe(Recipe *recipe) {
try {
SqlBackend::deleteRecipe(recipe->getId());
......@@ -295,7 +322,7 @@ void Backend::setupSyncFromKey(const QString &key) {
}
#ifdef Q_OS_ANDROID
void Backend::AndroidResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) {
void Backend::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) {
switch (receiverRequestCode) {
case static_cast<int>(AndroidRequestCodes::GetImage):
{
......@@ -304,7 +331,7 @@ void Backend::AndroidResultReceiver::handleActivityResult(int receiverRequestCod
} else {
QAndroidJniObject uri = data.callObjectMethod("getData", "()Landroid/net/Uri;");
QAndroidJniObject img = QAndroidJniObject::callStaticObjectMethod("org/jschwab/openrecipes/Helpers", "readImage", "(Landroid/net/Uri;Landroid/content/Context;)Ljava/lang/String;", uri.object(), QtAndroid::androidContext().object());
emit backend->openImage(img.toString());
emit openImage(img.toString());
}
}
break;
......@@ -315,7 +342,7 @@ void Backend::AndroidResultReceiver::handleActivityResult(int receiverRequestCod
} else {
QAndroidJniObject scanResult = QAndroidJniObject::fromString("SCAN_RESULT");
QString qrcode = data.callObjectMethod("getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;", scanResult.object<jstring>()).toString();
backend->setupSyncFromKey(qrcode);
setupSyncFromKey(qrcode);
}
}
default:
......@@ -332,17 +359,32 @@ void Backend::requestImage() {
QAndroidJniObject category = QAndroidJniObject::getStaticObjectField<jstring>("android/content/Intent", "CATEGORY_OPENABLE");
intent.handle().callObjectMethod("addCategory", "(Ljava/lang/String;)Landroid/content/Intent;", category.object<jstring>());
QtAndroid::startActivity(intent.handle(), static_cast<int>(AndroidRequestCodes::GetImage), &androidResultReceiver);
QtAndroid::startActivity(intent.handle(), static_cast<int>(AndroidRequestCodes::GetImage), this);
QAndroidJniEnvironment jniEnv;
if (jniEnv->ExceptionCheck()) jniEnv->ExceptionClear();
}
void Backend::requestQRCode() {
QAndroidIntent intent("com.google.zxing.client.android.SCAN");
QAndroidJniObject scanMode = QAndroidJniObject::fromString("SCAN_MODE");
QAndroidJniObject qrCodeMode = QAndroidJniObject::fromString("QR_CODE_MODE");
intent.handle().callObjectMethod("putExtra", "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;", scanMode.object<jstring>(), qrCodeMode.object<jstring>());
QtAndroid::startActivity(intent.handle(), static_cast<int>(AndroidRequestCodes::GetQRCode), &androidResultReceiver);
QtAndroid::startActivity(intent.handle(), static_cast<int>(AndroidRequestCodes::GetQRCode), this);
QAndroidJniEnvironment jniEnv;
if (jniEnv->ExceptionCheck()) {
/* The user has no fitting app, so we let him install one */
jniEnv->ExceptionClear();
QAndroidJniObject uriString = QAndroidJniObject::fromString("market://details?id=com.google.zxing.client.android");
QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", uriString.object<jstring>());
QAndroidIntent intent("android.intent.action.VIEW");
intent.handle().callObjectMethod("setData", "(Landroid/net/Uri;)Landroid/content/Intent;", uri.object());
QtAndroid::startActivity(intent.handle(), 0);
/* If we get an error here, there is no app store on the phone */
if (jniEnv->ExceptionCheck()) jniEnv->ExceptionClear();
}
}
void Backend::shareRecipe(Recipe *recipe) {
......@@ -352,7 +394,7 @@ void Backend::shareRecipe(Recipe *recipe) {
QAndroidJniObject metaType = QAndroidJniObject::fromString("openrecipes/recipe_xml");
intent.handle().callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", metaType.object<jstring>());
QAndroidJniObject contentUriString = QAndroidJniObject::fromString(QString("content://org.jschwab.openrecipes.provider/recipe_xml/%1").arg(idHex));
QAndroidJniObject contentUri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", contentUriString.object<jstring>());
QAndroidJniObject extraStream = QAndroidJniObject::getStaticObjectField<jstring>("android/content/Intent", "EXTRA_STREAM");
......@@ -362,6 +404,8 @@ void Backend::shareRecipe(Recipe *recipe) {
QAndroidJniObject chooser = QAndroidJniObject::callStaticObjectMethod("android/content/Intent", "createChooser", "(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;", intent.handle().object(), title.object<jstring>());
QtAndroid::startActivity(chooser, 0);
QAndroidJniEnvironment jniEnv;
if (jniEnv->ExceptionCheck()) jniEnv->ExceptionClear();
}
#else
bool Backend::importRecipeFromFile(const QString &path) {
......@@ -376,6 +420,7 @@ bool Backend::importRecipeFromFile(const QString &path) {
QTextStream stream(&f);
try {
Recipe *r = new Recipe(stream.readAll(), nullptr);
emit recipeImported(r->getId());
delete r;
} catch (const InternalError &e) {
return false;
......
......@@ -34,7 +34,11 @@ class SynchronizeAsync;
class SendSyncKeyAsync;
class RecvSyncKeyAsync;
#ifdef Q_OS_ANDROID
class Backend : public QObject, public QAndroidActivityResultReceiver {
#else
class Backend : public QObject {
#endif
Q_OBJECT
Q_PROPERTY(QQmlListProperty<Recipe> recipesList READ getRecipesList NOTIFY recipesListChanged)
Q_PROPERTY(QString syncKeyHex READ getSyncKeyHex NOTIFY syncSettingsChanged);
......@@ -61,16 +65,7 @@ class Backend : public QObject {
GetQRCode
};
class AndroidResultReceiver : public QAndroidActivityResultReceiver {
private:
Backend *const backend;
void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) final;
public:
explicit AndroidResultReceiver(Backend *const backend) : backend(backend) {};
};
AndroidResultReceiver androidResultReceiver;
void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override;
#endif
public:
......@@ -93,7 +88,9 @@ class Backend : public QObject {
Q_INVOKABLE void setupSync() const;
Q_INVOKABLE void removeSyncSettings() const;
bool getSyncAvailable() const;
Q_INVOKABLE Recipe* getRecipe(const QByteArray &id);
Q_INVOKABLE Recipe* getNewRecipe();
Q_INVOKABLE int getRecipeListIndex(const QByteArray &id);
Q_INVOKABLE bool deleteRecipe(Recipe *recipe);
Q_INVOKABLE void synchronize();
Q_INVOKABLE void stopSynchronizing();
......@@ -123,6 +120,7 @@ class Backend : public QObject {
void openImage(const QUrl &src);
void syncSettingsChanged();
void filterChanged();
void recipeImported(const QByteArray &id);
};
#endif //BACKEND_H
......@@ -5,7 +5,7 @@
#include <QAndroidJniObject>
#include <QCoreApplication>
JNIEXPORT jboolean JNICALL Java_org_jschwab_openrecipes_Helpers_importRecipe (JNIEnv *, jclass, jstring recipeXml) {
JNIEXPORT jboolean JNICALL Java_org_jschwab_openrecipes_Helpers_importRecipe(JNIEnv *, jclass, jstring recipeXml) {
QAndroidJniObject xml = QAndroidJniObject::fromLocalRef(recipeXml);
try {
if (QCoreApplication::startingUp()) {
......@@ -15,6 +15,7 @@ JNIEXPORT jboolean JNICALL Java_org_jschwab_openrecipes_Helpers_importRecipe (JN
} else {
qInfo() << "Importing recipes";
Recipe *r = new Recipe(xml.toString(), nullptr);
emit Jni::getNotifier()->recipeImported(r->getId());
delete r;
}
} catch (const InternalError &e) {
......@@ -23,7 +24,7 @@ JNIEXPORT jboolean JNICALL Java_org_jschwab_openrecipes_Helpers_importRecipe (JN
return true;
}
JNIEXPORT jstring JNICALL Java_org_jschwab_openrecipes_Helpers_getRecipeXml (JNIEnv *env, jclass, jstring recipeIdHex) {
JNIEXPORT jstring JNICALL Java_org_jschwab_openrecipes_Helpers_getRecipeXml(JNIEnv *env, jclass, jstring recipeIdHex) {
if (QCoreApplication::startingUp()) return reinterpret_cast<jstring>(env->NewLocalRef(QAndroidJniObject::fromString("").object<jstring>()));
QAndroidJniObject idHex = QAndroidJniObject::fromLocalRef(recipeIdHex);
try {
......@@ -36,7 +37,7 @@ JNIEXPORT jstring JNICALL Java_org_jschwab_openrecipes_Helpers_getRecipeXml (JNI
}
}
JNIEXPORT jstring JNICALL Java_org_jschwab_openrecipes_Helpers_getRecipeName (JNIEnv *env, jclass, jstring recipeIdHex) {
JNIEXPORT jstring JNICALL Java_org_jschwab_openrecipes_Helpers_getRecipeName(JNIEnv *env, jclass, jstring recipeIdHex) {
if (QCoreApplication::startingUp()) return reinterpret_cast<jstring>(env->NewLocalRef(QAndroidJniObject::fromString("").object<jstring>()));
QAndroidJniObject idHex = QAndroidJniObject::fromLocalRef(recipeIdHex);
try {
......@@ -47,5 +48,11 @@ JNIEXPORT jstring JNICALL Java_org_jschwab_openrecipes_Helpers_getRecipeName (JN
}
}
std::shared_ptr<JniNotifier> Jni::getNotifier() {
if (!notifier) notifier = std::make_shared<JniNotifier>();
return notifier;
}
std::forward_list<QString> Jni::importBuffer;
QMutex Jni::importBufferMutex;
std::shared_ptr<JniNotifier> Jni::notifier;
......@@ -5,19 +5,32 @@
#include <forward_list>
#include <QString>
#include <QMutex>
#include <QObject>
#include <memory>
class JniNotifier : public QObject {
Q_OBJECT
signals:
void recipeImported(const QByteArray &id);
};
class Jni {
private:
static std::shared_ptr<JniNotifier> notifier;
public:
static std::forward_list<QString> importBuffer;
static QMutex importBufferMutex;
static std::shared_ptr<JniNotifier> getNotifier();
};
extern "C" {
JNIEXPORT jboolean JNICALL Java_org_jschwab_openrecipes_Helpers_importRecipe (JNIEnv *, jclass, jstring);
JNIEXPORT jboolean JNICALL Java_org_jschwab_openrecipes_Helpers_importRecipe(JNIEnv*, jclass, jstring);
JNIEXPORT jstring JNICALL Java_org_jschwab_openrecipes_Helpers_getRecipeXml (JNIEnv *, jclass, jstring);
JNIEXPORT jstring JNICALL Java_org_jschwab_openrecipes_Helpers_getRecipeXml(JNIEnv*, jclass, jstring);
JNIEXPORT jstring JNICALL Java_org_jschwab_openrecipes_Helpers_getRecipeName (JNIEnv *, jclass, jstring);
JNIEXPORT jstring JNICALL Java_org_jschwab_openrecipes_Helpers_getRecipeName(JNIEnv*, jclass, jstring);
}
#endif //JNI_H
......@@ -227,6 +227,9 @@ void SqlBackend::connectSignals(SqlTransaction *trans) {
emit transmitter->recipeChanged(QByteArray());
emit transmitter->ingredientChanged(-1);
});
connect(trans, &SqlTransaction::committed, transmitter.get(), []() {
emit transmitter->recipesListChanged();
});
}
template<class T, class S>
......
......@@ -111,6 +111,7 @@ int main(int argc, char **argv) {
for (const auto &xml : Jni::importBuffer) {
try {
Recipe *r = new Recipe(xml.toUtf8(), nullptr);
emit Jni::getNotifier()->recipeImported(r->getId());
delete r;
} catch (const InternalError &e) {
qWarning() << "Can't import buffered recipe";
......
CONFIG += release
DEFINES += QT_DEPRECATED_WARNINGS QT_NO_DEBUG_OUTPUT
VERSION = 0.2.1
ANDROID_VERSION_CODE = 3
DEFINES += QT_DEPRECATED_WARNINGS
unix: QMAKE_CXXFLAGS += -Wextra -Werror=format-security -D_FORTIFY_SECURITY=2
VERSION = 0.2.0
CONFIG(release, debug|release): DEFINES += QT_NO_DEBUG_OUTPUT
CONFIG(debug, debug|release) {
CONFIG += qml_debug declaratice_debug warn_on
unix: QMAKE_CXXFLAGS += -Werror
}
CONFIG(debug, debug|release): NAMEPOSTFIX="_debug"
CONFIG(release, debug|release): NAMEPOSTFIX=""
......
......@@ -81,10 +81,12 @@ class SqlTransaction : public QObject {
commited = true;
if (!SqlBackendBase::getDatabase().commit()) throw IERROR("Can't commit");
qDebug() << "Commit sql transaction";
emit committed();
}
signals:
void rolledBack();
void committed();
};
class SqlNoResult {};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment