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:
Aleix Pol 2018-05-09 02:45:12 +02:00
parent aa800c01bc
commit cf4bb5e3b1
12 changed files with 172 additions and 36 deletions

View file

@ -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() )

View file

@ -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>

View 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());
}
}

View file

@ -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);

View file

@ -96,7 +96,7 @@ Item {
return;
}
documentItem.path = model.filePath;
documentItem.url = model.fileURL;
globalDrawer.close();
applicationWindow().controlsVisible = false;
}

View file

@ -47,7 +47,7 @@ Kirigami.OverlayDrawer {
Connections {
target: documentItem
onPathChanged: thumbnailsButton.checked = true;
onUrlChanged: thumbnailsButton.checked = true;
}
QQC2.ToolBar {

View file

@ -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();
}
}

View file

@ -50,7 +50,7 @@ QtControls.ScrollView {
}
Connections {
target: root.document
onPathChanged: resizeTimer.restart()
onUrlChanged: resizeTimer.restart()
}
Timer {
id: resizeTimer

View file

@ -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)

View file

@ -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();

View file

@ -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

View file

@ -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 )