diff --git a/mobile/android/AndroidManifest.xml b/mobile/android/AndroidManifest.xml index 5e889e1fd..d8689ea21 100644 --- a/mobile/android/AndroidManifest.xml +++ b/mobile/android/AndroidManifest.xml @@ -1,75 +1,48 @@ + - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mobile/android/src/OpenFileActivity.java b/mobile/android/src/OpenFileActivity.java index 9971fa75d..c92e4aac1 100644 --- a/mobile/android/src/OpenFileActivity.java +++ b/mobile/android/src/OpenFileActivity.java @@ -1,43 +1,79 @@ package org.kde.something; import android.content.ContentResolver; import android.content.Intent; import android.util.Log; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.net.Uri; +import android.app.Activity; import org.qtproject.qt5.android.bindings.QtActivity; class FileClass { public static native void openUri(String uri); } -public class OpenFileActivity extends QtActivity { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - final Intent bundleIntent = getIntent(); - if (bundleIntent == null) +public class OpenFileActivity extends QtActivity +{ + private void displayUri(Uri uri) + { + if (uri == null) return; - final String action = bundleIntent.getAction(); - Uri uri = bundleIntent.getData(); if (!uri.getScheme().equals("file")) { try { ContentResolver resolver = getBaseContext().getContentResolver(); ParcelFileDescriptor fdObject = resolver.openFileDescriptor(uri, "r"); uri = Uri.parse("fd:///" + fdObject.detachFd()); } catch (Exception e) { e.printStackTrace(); //TODO: emit warning that couldn't be opened - Log.v("Okular", "failed to open"); + Log.e("Okular", "failed to open"); return; } } + Log.e("Okular", "opening url: " + uri.toString()); FileClass.openUri(uri.toString()); } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final Intent bundleIntent = getIntent(); + if (bundleIntent == null) + return; + + final String action = bundleIntent.getAction(); + Log.v("Okular", "Starting action: " + action); + if (action == "android.intent.action.VIEW") { + displayUri(bundleIntent.getData()); + } + } + + private static int OpenDocumentRequest = 42; + + public static void openFile(Activity context, String title, String mimes) + { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_GET_CONTENT); + intent.setType("application/pdf"); + Log.v("Okular", "opening: " + mimes); + intent.putExtra(Intent.EXTRA_MIME_TYPES, mimes.split(";")); + + context.startActivityForResult(intent, OpenDocumentRequest); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + Log.v("Okular", "Activity Result: " + String.valueOf(requestCode) + " with code: " + String.valueOf(resultCode)); + if (requestCode == OpenDocumentRequest) { + Uri uri = intent.getData(); + Log.v("Okular", "Opening document: " + uri.toString()); + displayUri(uri); + } + } } diff --git a/mobile/app/CMakeLists.txt b/mobile/app/CMakeLists.txt index f4f06ef88..3def6ef53 100644 --- a/mobile/app/CMakeLists.txt +++ b/mobile/app/CMakeLists.txt @@ -1,11 +1,13 @@ set(CMAKE_AUTORCC ON) add_executable(okularkirigami main.cpp app.qrc) target_link_libraries(okularkirigami Qt5::Widgets Qt5::Qml KF5::I18n) if (ANDROID) + find_package(Qt5 COMPONENTS AndroidExtras) target_sources(okularkirigami PRIVATE android.cpp) + target_link_libraries(okularkirigami Qt5::AndroidExtras) endif() install(TARGETS okularkirigami ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES package/metadata.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} RENAME org.kde.okular.kirigami.desktop) install( FILES org.kde.okular.kirigami.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR} ) diff --git a/mobile/app/android.cpp b/mobile/app/android.cpp index 40afc3982..8a91e35ce 100644 --- a/mobile/app/android.cpp +++ b/mobile/app/android.cpp @@ -1,30 +1,60 @@ /************************************************************************************* * Copyright (C) 2018 by Aleix Pol * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "android.h" +#include +#include +#include +#include + +static AndroidInstance* s_instance = nullptr; + +void AndroidInstance::openFile(const QString &title, const QStringList &mimes) +{ + s_instance = this; + QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); //activity is valid + Q_ASSERT ( activity.isValid() ); + + QAndroidJniEnvironment _env; + QAndroidJniObject::callStaticMethod("org/kde/something/OpenFileActivity", + "openFile", + "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)V", + activity.object(), + QAndroidJniObject::fromString(title).object(), + QAndroidJniObject::fromString(mimes.join(';')).object() + ); + if (_env->ExceptionCheck()) { + _env->ExceptionClear(); + qWarning() << "couldn't launch intent"; + } +} void Java_org_kde_something_FileClass_openUri(JNIEnv *env, jobject /*obj*/, jstring uri) { jboolean isCopy = false; const char* utf = env->GetStringUTFChars(uri, &isCopy); - handler.openUri(QString::fromUtf8(utf)); + const QString uriString = QString::fromUtf8(utf); + if (s_instance) + s_instance->openUri(QUrl(uriString)); + else + handler.openUri(uriString); env->ReleaseStringUTFChars(uri, utf); } diff --git a/mobile/app/android.h b/mobile/app/android.h index 358c70f0e..7c456084b 100644 --- a/mobile/app/android.h +++ b/mobile/app/android.h @@ -1,45 +1,56 @@ /************************************************************************************* * Copyright (C) 2018 by Aleix Pol * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #ifndef ANDROID_H #define ANDROID_H -#include +#include +#include #include class URIHandler { public: void openUri(const QString &uri) { m_lastUrl = uri; } QString m_lastUrl; }; static URIHandler handler; +class AndroidInstance : public QObject +{ + Q_OBJECT +public: + Q_SCRIPTABLE void openFile(const QString &title, const QStringList &mimes); + +Q_SIGNALS: + void openUri(const QUrl &uri); +}; + extern "C" { JNIEXPORT void JNICALL Java_org_kde_something_FileClass_openUri(JNIEnv *env, jobject /*obj*/, jstring uri); } #endif diff --git a/mobile/app/main.cpp b/mobile/app/main.cpp index 139c587b2..57fdf72ea 100644 --- a/mobile/app/main.cpp +++ b/mobile/app/main.cpp @@ -1,72 +1,74 @@ /************************************************************************************* * Copyright (C) 2010 by Aleix Pol * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __ANDROID__ #include "android.h" Q_DECL_EXPORT #endif int main(int argc, char *argv[]) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication app(argc, argv); app.setApplicationName(QStringLiteral("okularkirigami")); QCommandLineParser parser; parser.addVersionOption(); parser.addHelpOption(); //parser.setApplicationDescription(i18n("Okular mobile")); parser.process(app); QQmlApplicationEngine engine; #ifdef __ANDROID__ + qmlRegisterSingletonType("org.kde.okular.app", 2, 0, "AndroidInstance", [](QQmlEngine*, QJSEngine*) -> QObject* { return new AndroidInstance; }); const QString uri = handler.m_lastUrl; #else + qmlRegisterSingletonType("org.kde.okular.app", 2, 0, "AndroidInstance", [](QQmlEngine*, QJSEngine*) -> QObject* { return new QObject; }); const QString uri = parser.positionalArguments().count() == 1 ? QUrl::fromUserInput(parser.positionalArguments().constFirst(), {}, QUrl::AssumeLocalFile).toString() : QString(); #endif engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); engine.rootContext()->setContextProperty(QStringLiteral("uri"), uri); QVariantMap paths; paths[QStringLiteral("desktop")] = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); paths[QStringLiteral("documents")] = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); paths[QStringLiteral("music")] = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); paths[QStringLiteral("movies")] = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); paths[QStringLiteral("pictures")] = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); paths[QStringLiteral("home")] = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); engine.rootContext()->setContextProperty(QStringLiteral("userPaths"), paths); engine.setBaseUrl(QUrl("qrc:/package/contents/ui/")); engine.load(QUrl("qrc:/package/contents/ui/main.qml")); return app.exec(); } diff --git a/mobile/app/package/contents/ui/main.qml b/mobile/app/package/contents/ui/main.qml index 3091b573f..b25cb3d74 100644 --- a/mobile/app/package/contents/ui/main.qml +++ b/mobile/app/package/contents/ui/main.qml @@ -1,79 +1,96 @@ /* * Copyright 2012 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.1 import QtQuick.Dialogs 1.3 as QQD import org.kde.okular 2.0 as Okular import org.kde.kirigami 2.0 as Kirigami +import org.kde.okular.app 2.0 Kirigami.AbstractApplicationWindow { id: fileBrowserRoot visible: true header: null globalDrawer: Kirigami.GlobalDrawer { title: i18n("Okular") titleIcon: "okular" QQD.FileDialog { id: fileDialog nameFilters: Okular.Okular.nameFilters folder: "file://" + userPaths.documents onAccepted: { documentItem.url = fileDialog.fileUrl } } actions: [ Kirigami.Action { text: i18n("Open...") icon.name: "document-open" onTriggered: { fileDialog.open() } + }, + Kirigami.Action { + text: i18n("Open Android...") + icon.name: "document-open" + readonly property var p0: Connections { + target: AndroidInstance + enabled: AndroidInstance.hasOwnProperty("openFile") + onOpenUri: { + console.log("open uri!", uri) + documentItem.url = uri + } + } + onTriggered: { +// var mimetypes = Okular.Okular.mimeTypes.join(",") + AndroidInstance.openFile(i18n("Document to open..."), "*/*") + } } ] } contextDrawer: OkularDrawer {} title: documentItem.windowTitleForDocument Okular.DocumentItem { id: documentItem onUrlChanged: { currentPage = 0 } } MainView { id: pageArea anchors.fill: parent document: documentItem } //FIXME: this is due to global vars being binded after the parse is done, do the 2 steps parsing Timer { interval: 100 running: true onTriggered: { if (uri) { documentItem.url = uri } else { globalDrawer.open(); } } } }