mirror of
https://invent.kde.org/graphics/okular
synced 2024-10-02 14:14:10 +00:00
Android: Make it possible to associate with files
Summary: Put the file into a file descriptor and open this file descriptor. Just application/pdf for now. Test Plan: opened some documents on my phone Reviewers: #okular, aacid, mart Reviewed By: mart Subscribers: okular-devel Tags: #okular Differential Revision: https://phabricator.kde.org/D12770
This commit is contained in:
parent
aa800c01bc
commit
cf4bb5e3b1
|
@ -2363,9 +2363,22 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
QMimeDatabase db;
|
||||
QMimeType mime = _mime;
|
||||
QByteArray filedata;
|
||||
bool isstdin = url.fileName() == QLatin1String( "-" );
|
||||
int fd = -1;
|
||||
if (url.scheme() == QLatin1String("fd"))
|
||||
{
|
||||
bool ok;
|
||||
fd = url.path().mid(1).toInt(&ok);
|
||||
if (!ok)
|
||||
{
|
||||
return OpenError;
|
||||
}
|
||||
}
|
||||
else if (url.fileName() == QLatin1String( "-" ))
|
||||
{
|
||||
fd = 0;
|
||||
}
|
||||
bool triedMimeFromFileContent = false;
|
||||
if ( !isstdin )
|
||||
if ( fd < 0 )
|
||||
{
|
||||
if ( !mime.isValid() )
|
||||
return OpenError;
|
||||
|
@ -2379,7 +2392,12 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
else
|
||||
{
|
||||
QFile qstdin;
|
||||
qstdin.open( stdin, QIODevice::ReadOnly );
|
||||
const bool ret = qstdin.open( fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle );
|
||||
if (!ret) {
|
||||
qWarning() << "failed to read" << url << filedata;
|
||||
return OpenError;
|
||||
}
|
||||
|
||||
filedata = qstdin.readAll();
|
||||
mime = db.mimeTypeForData( filedata );
|
||||
if ( !mime.isValid() || mime.isDefault() )
|
||||
|
@ -2388,6 +2406,8 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
triedMimeFromFileContent = true;
|
||||
}
|
||||
|
||||
const bool fromFileDescriptor = fd >= 0;
|
||||
|
||||
// 0. load Generator
|
||||
// request only valid non-disabled plugins suitable for the mimetype
|
||||
KPluginMetaData offer = DocumentPrivate::generatorForMimeType(mime, d->m_widget);
|
||||
|
@ -2422,7 +2442,7 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
}
|
||||
|
||||
// 1. load Document
|
||||
OpenResult openResult = d->openDocumentInternal( offer, isstdin, docFile, filedata, password );
|
||||
OpenResult openResult = d->openDocumentInternal( offer, fromFileDescriptor, docFile, filedata, password );
|
||||
if ( openResult == OpenError )
|
||||
{
|
||||
QVector<KPluginMetaData> triedOffers;
|
||||
|
@ -2431,7 +2451,7 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
|
||||
while ( offer.isValid() )
|
||||
{
|
||||
openResult = d->openDocumentInternal( offer, isstdin, docFile, filedata, password );
|
||||
openResult = d->openDocumentInternal( offer, fromFileDescriptor, docFile, filedata, password );
|
||||
|
||||
if ( openResult == OpenError )
|
||||
{
|
||||
|
@ -2451,7 +2471,7 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
offer = DocumentPrivate::generatorForMimeType(mime, d->m_widget, triedOffers);
|
||||
while ( offer.isValid() )
|
||||
{
|
||||
openResult = d->openDocumentInternal( offer, isstdin, docFile, filedata, password );
|
||||
openResult = d->openDocumentInternal( offer, fromFileDescriptor, docFile, filedata, password );
|
||||
|
||||
if ( openResult == OpenError )
|
||||
{
|
||||
|
@ -2553,7 +2573,7 @@ Document::OpenResult Document::openDocument(const QString & docFile, const QUrl
|
|||
d->m_nextDocumentDestination = QString();
|
||||
}
|
||||
|
||||
AudioPlayer::instance()->d->m_currentDocument = isstdin ? QUrl() : d->m_url;
|
||||
AudioPlayer::instance()->d->m_currentDocument = fromFileDescriptor ? QUrl() : d->m_url;
|
||||
|
||||
const QStringList docScripts = d->m_generator->metaData( QStringLiteral("DocumentScripts"), QStringLiteral ( "JavaScript" ) ).toStringList();
|
||||
if ( !docScripts.isEmpty() )
|
||||
|
|
|
@ -4,11 +4,47 @@
|
|||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
|
||||
android:name="org.qtproject.qt5.android.bindings.QtActivity"
|
||||
android:label="@string/app_name"
|
||||
android:hardwareAccelerated="true"
|
||||
android:screenOrientation="unspecified">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="okularkirigami"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<!-- Deploy Qt libs as part of package -->
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/>
|
||||
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
|
||||
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
|
||||
<!-- Run with local libs -->
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="jar/QtAndroid.jar:jar/QtAndroidAccessibility.jar:jar/QtAndroid-bundled.jar:jar/QtAndroidAccessibility-bundled.jar:jar/QtAndroidBearer.jar:jar/QtAndroidBearer-bundled.jar"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value=""/>
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||
<!-- Messages maps -->
|
||||
</activity>
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
|
||||
android:name="org.kde.something.OpenFileActivity"
|
||||
android:label="@string/app_name"
|
||||
android:hardwareAccelerated="true"
|
||||
android:screenOrientation="unspecified">
|
||||
|
||||
<intent-filter android:label="Okular">
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:mimeType="application/pdf"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="okularkirigami"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
|
@ -32,5 +68,8 @@
|
|||
</activity>
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="16" />
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
</manifest>
|
||||
|
|
43
mobile/android/src/OpenFileActivity.java
Normal file
43
mobile/android/src/OpenFileActivity.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
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 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)
|
||||
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");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FileClass.openUri(uri.toString());
|
||||
}
|
||||
}
|
|
@ -28,12 +28,40 @@
|
|||
#include <QQmlApplicationEngine>
|
||||
#include <QCommandLineParser>
|
||||
#include <QIcon>
|
||||
#include <QTimer>
|
||||
|
||||
Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
qputenv("QT_QUICK_CONTROLS_STYLE", "material");
|
||||
#include <jni.h>
|
||||
|
||||
class URIHandler : public QObject {
|
||||
public:
|
||||
void openUri(const QString &uri) {
|
||||
m_lastUrl = uri;
|
||||
}
|
||||
|
||||
QString m_lastUrl;
|
||||
} handler;
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
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));
|
||||
env->ReleaseStringUTFChars(uri, utf);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT
|
||||
#endif
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QApplication app(argc, argv);
|
||||
app.setApplicationName(QStringLiteral("okularkirigami"));
|
||||
|
@ -43,10 +71,17 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
|||
parser.addHelpOption();
|
||||
//parser.setApplicationDescription(i18n("Okular mobile"));
|
||||
parser.process(app);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
const QString uri = handler.m_lastUrl;
|
||||
#else
|
||||
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("commandlineArguments"), parser.positionalArguments());
|
||||
engine.rootContext()->setContextProperty(QStringLiteral("uri"), uri);
|
||||
QVariantMap paths;
|
||||
paths[QStringLiteral("desktop")] = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
paths[QStringLiteral("documents")] = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
|
|
|
@ -96,7 +96,7 @@ Item {
|
|||
return;
|
||||
}
|
||||
|
||||
documentItem.path = model.filePath;
|
||||
documentItem.url = model.fileURL;
|
||||
globalDrawer.close();
|
||||
applicationWindow().controlsVisible = false;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ Kirigami.OverlayDrawer {
|
|||
|
||||
Connections {
|
||||
target: documentItem
|
||||
onPathChanged: thumbnailsButton.checked = true;
|
||||
onUrlChanged: thumbnailsButton.checked = true;
|
||||
}
|
||||
|
||||
QQC2.ToolBar {
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.kde.kirigami 2.0 as Kirigami
|
|||
|
||||
Kirigami.AbstractApplicationWindow {
|
||||
id: fileBrowserRoot
|
||||
objectName: "fileBrowserRoot"
|
||||
visible: true
|
||||
|
||||
/*TODO: port ResourceInstance
|
||||
|
@ -44,11 +43,11 @@ Kirigami.AbstractApplicationWindow {
|
|||
drawerOpen: false
|
||||
}
|
||||
|
||||
title: documentItem.windowTitleForDocument
|
||||
Okular.DocumentItem {
|
||||
id: documentItem
|
||||
onPathChanged: currentPage = 0
|
||||
onUrlChanged: { currentPage = 0 }
|
||||
onWindowTitleForDocumentChanged: {
|
||||
fileBrowserRoot.title = windowTitleForDocument
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,11 +62,9 @@ Kirigami.AbstractApplicationWindow {
|
|||
interval: 100
|
||||
running: true
|
||||
onTriggered: {
|
||||
if (commandlineArguments.length > 0) {
|
||||
documentItem.path = commandlineArguments[0]
|
||||
}
|
||||
|
||||
if (commandlineArguments.length == 0) {
|
||||
if (uri) {
|
||||
documentItem.url = uri
|
||||
} else {
|
||||
globalDrawer.open();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ QtControls.ScrollView {
|
|||
}
|
||||
Connections {
|
||||
target: root.document
|
||||
onPathChanged: resizeTimer.restart()
|
||||
onUrlChanged: resizeTimer.restart()
|
||||
}
|
||||
Timer {
|
||||
id: resizeTimer
|
||||
|
|
|
@ -53,12 +53,14 @@ DocumentItem::~DocumentItem()
|
|||
delete m_document;
|
||||
}
|
||||
|
||||
void DocumentItem::setPath(const QString &path)
|
||||
void DocumentItem::setUrl(const QUrl & url)
|
||||
{
|
||||
//TODO: remote urls
|
||||
//TODO: password
|
||||
QMimeDatabase db;
|
||||
m_document->openDocument(path, QUrl::fromLocalFile(path), db.mimeTypeForUrl(QUrl::fromLocalFile(path)));
|
||||
|
||||
const QString path = url.isLocalFile() ? url.toLocalFile() : QLatin1String("-");
|
||||
|
||||
m_document->openDocument(path, url, db.mimeTypeForUrl(url));
|
||||
|
||||
m_tocModel->clear();
|
||||
m_tocModel->fill(m_document->documentSynopsis());
|
||||
|
@ -69,7 +71,7 @@ void DocumentItem::setPath(const QString &path)
|
|||
m_matchingPages << (int)i;
|
||||
}
|
||||
emit matchingPagesChanged();
|
||||
emit pathChanged();
|
||||
emit urlChanged();
|
||||
emit pageCountChanged();
|
||||
emit openedChanged();
|
||||
emit supportsSearchingChanged();
|
||||
|
@ -95,9 +97,9 @@ QString DocumentItem::windowTitleForDocument() const
|
|||
return title;
|
||||
}
|
||||
|
||||
QString DocumentItem::path() const
|
||||
QUrl DocumentItem::url() const
|
||||
{
|
||||
return m_document->currentDocument().toDisplayString();
|
||||
return m_document->currentDocument();
|
||||
}
|
||||
|
||||
void DocumentItem::setCurrentPage(int page)
|
||||
|
|
|
@ -39,9 +39,9 @@ class DocumentItem : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
/**
|
||||
* Absolute path of the document file to open
|
||||
* Absolute URI to document file to open
|
||||
*/
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
|
||||
|
||||
/**
|
||||
* Suggested window title if a window represents this document. may be pathname or document title, dependeing from Okular settings.
|
||||
|
@ -98,8 +98,8 @@ public:
|
|||
explicit DocumentItem(QObject *parent=nullptr);
|
||||
~DocumentItem();
|
||||
|
||||
void setPath(const QString &path);
|
||||
QString path() const;
|
||||
void setUrl(const QUrl &url);
|
||||
QUrl url() const;
|
||||
|
||||
QString windowTitleForDocument() const;
|
||||
|
||||
|
@ -141,7 +141,7 @@ public:
|
|||
Observer *thumbnailObserver();
|
||||
|
||||
Q_SIGNALS:
|
||||
void pathChanged();
|
||||
void urlChanged();
|
||||
void pageCountChanged();
|
||||
void openedChanged();
|
||||
void searchInProgressChanged();
|
||||
|
|
|
@ -121,7 +121,7 @@ void PageItem::setDocument(DocumentItem *doc)
|
|||
emit documentChanged();
|
||||
m_redrawTimer->start();
|
||||
|
||||
connect(doc, &DocumentItem::pathChanged, this, &PageItem::refreshPage);
|
||||
connect(doc, &DocumentItem::urlChanged, this, &PageItem::refreshPage);
|
||||
}
|
||||
|
||||
int PageItem::pageNumber() const
|
||||
|
|
|
@ -278,7 +278,7 @@ void Shell::openUrl( const QUrl & url, const QString &serializedOptions )
|
|||
m_tabWidget->setTabText( activeTab, url.fileName() );
|
||||
applyOptionsToPart( activePart, serializedOptions );
|
||||
bool openOk = activePart->openUrl( url );
|
||||
const bool isstdin = url.fileName() == QLatin1String( "-" );
|
||||
const bool isstdin = url.fileName() == QLatin1String( "-" ) || url.scheme() == QLatin1String( "fd" );
|
||||
if ( !isstdin )
|
||||
{
|
||||
if ( openOk )
|
||||
|
|
Loading…
Reference in a new issue