Skip to content

Commit 26014a1

Browse files
authored
Merge pull request #129 from virxkane/issue126
Issue126
2 parents 9ab7e6c + 97961c2 commit 26014a1

File tree

9 files changed

+380
-68
lines changed

9 files changed

+380
-68
lines changed

android/jni/cr3engine.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,7 @@ static JNINativeMethod sDocViewMethods[] = {
857857
{"destroyInternal", "()V", (void*)Java_org_coolreader_crengine_DocView_destroyInternal},
858858
{"getPageImageInternal", "(Landroid/graphics/Bitmap;I)V", (void*)Java_org_coolreader_crengine_DocView_getPageImageInternal},
859859
{"loadDocumentInternal", "(Ljava/lang/String;)Z", (void*)Java_org_coolreader_crengine_DocView_loadDocumentInternal},
860+
{"loadDocumentFromMemoryInternal", "([BLjava/lang/String;)Z", (void*)Java_org_coolreader_crengine_DocView_loadDocumentFromMemoryInternal},
860861
{"getSettingsInternal", "()Ljava/util/Properties;", (void*)Java_org_coolreader_crengine_DocView_getSettingsInternal},
861862
{"applySettingsInternal", "(Ljava/util/Properties;)Z", (void*)Java_org_coolreader_crengine_DocView_applySettingsInternal},
862863
{"setStylesheetInternal", "(Ljava/lang/String;)V", (void*)Java_org_coolreader_crengine_DocView_setStylesheetInternal},

android/jni/docview.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,25 @@ bool DocViewNative::loadDocument( lString16 filename )
891891
return res;
892892
}
893893

894+
bool DocViewNative::loadDocument( LVStreamRef stream, lString16 contentPath )
895+
{
896+
CRLog::info("Loading document from memory stream, content path: %s", LCSTR(contentPath));
897+
bool res = _docview->LoadDocument(stream, contentPath.c_str(), false);
898+
if (res)
899+
CRLog::info("Document %s is loaded successfully", LCSTR(contentPath));
900+
else {
901+
CRLog::info("Document %s not is loaded due to error", LCSTR(contentPath));
902+
if (_docview->getDocument() == NULL) {
903+
// _docview->LoadDocument() can return false with _docview->m_doc == NULL when:
904+
// 1. I/O error - failed to open file
905+
// 2. open archive without supported files
906+
CRLog::error("Document is NULL, inserting stub.");
907+
_docview->createDefaultDocument(lString16::empty_str, Utf8ToUnicode("Error while opening file!"));
908+
}
909+
}
910+
return res;
911+
}
912+
894913
bool DocViewNative::openRecentBook()
895914
{
896915
CRLog::debug("DocViewNative::openRecentBook()");
@@ -1331,6 +1350,27 @@ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_DocView_loadDocumentInte
13311350
return res ? JNI_TRUE : JNI_FALSE;
13321351
}
13331352

1353+
/*
1354+
* Class: org_coolreader_crengine_DocView
1355+
* Method: loadDocumentFromMemoryInternal
1356+
* Signature: ([BLjava/lang/String;)Z
1357+
*/
1358+
JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_DocView_loadDocumentFromMemoryInternal
1359+
(JNIEnv * _env, jobject _this, jbyteArray buf, jstring contentPath)
1360+
{
1361+
CRJNIEnv env(_env);
1362+
DocViewNative * p = getNative(_env, _this);
1363+
if (!p) {
1364+
CRLog::error("Cannot get native view");
1365+
return JNI_FALSE;
1366+
}
1367+
DocViewCallback callback( _env, p->_docview, _this );
1368+
LVStreamRef stream = env.jbyteArrayToStream(buf);
1369+
lString16 contentPath16 = env.fromJavaString(contentPath);
1370+
bool res = p->loadDocument(stream, contentPath16);
1371+
return res ? JNI_TRUE : JNI_FALSE;
1372+
}
1373+
13341374
/*
13351375
* Class: org_coolreader_crengine_DocView
13361376
* Method: getSettings

android/jni/docview.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class DocViewNative {
3030
bool saveHistory( lString16 filename );
3131
void createDefaultDocument( lString16 title, lString16 message );
3232
bool loadDocument( lString16 filename );
33+
bool loadDocument( LVStreamRef stream, lString16 contentPath );
3334
int doCommand( int cmd, int param );
3435
bool findText( lString16 pattern, int origin, bool reverse, bool caseInsensitive );
3536
void clearSelection();

android/jni/org_coolreader_crengine_DocView.h

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

android/src/org/coolreader/CoolReader.java

Lines changed: 116 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Main Class
22
package org.coolreader;
33

4+
import java.io.File;
5+
import java.io.FileNotFoundException;
6+
import java.io.InputStream;
47
import java.lang.reflect.Field;
58
import java.util.ArrayList;
69
import java.util.Date;
@@ -43,6 +46,7 @@
4346

4447
import android.Manifest;
4548
import android.content.BroadcastReceiver;
49+
import android.content.ContentResolver;
4650
import android.content.Context;
4751
import android.content.DialogInterface;
4852
import android.content.Intent;
@@ -275,16 +279,6 @@ public void setFullscreen(boolean fullscreen) {
275279
mReaderFrame.updateFullscreen(fullscreen);
276280
}
277281

278-
private String extractFileName(Uri uri) {
279-
if (uri != null) {
280-
if (uri.equals(Uri.parse("file:///")))
281-
return null;
282-
else
283-
return uri.getPath();
284-
}
285-
return null;
286-
}
287-
288282
@Override
289283
protected void onNewIntent(Intent intent) {
290284
log.i("onNewIntent : " + intent);
@@ -300,63 +294,19 @@ private boolean processIntent(Intent intent) {
300294
if (intent == null)
301295
return false;
302296
String fileToOpen = null;
303-
String scheme = null;
304-
String host = null;
297+
Uri uri = null;
305298
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
306-
Uri uri = intent.getData();
299+
uri = intent.getData();
307300
intent.setData(null);
308301
if (uri != null) {
309-
scheme = uri.getScheme();
310-
host = uri.getHost();
311-
if (uri.getEncodedPath().contains("%00"))
312-
fileToOpen = uri.getEncodedPath();
313-
else
314-
fileToOpen = uri.getPath();
315-
// if (fileToOpen.startsWith("file://"))
316-
// fileToOpen = fileToOpen.substring("file://".length());
302+
fileToOpen = filePathFromUri(uri);
317303
}
318304
}
319305
if (fileToOpen == null && intent.getExtras() != null) {
320306
log.d("extras=" + intent.getExtras());
321307
fileToOpen = intent.getExtras().getString(OPEN_FILE_PARAM);
322308
}
323309
if (fileToOpen != null) {
324-
// parse uri from system filemanager
325-
if (fileToOpen.contains("%00")) {
326-
// splitter between archive file name and inner file.
327-
fileToOpen = fileToOpen.replace("%00", "@/");
328-
fileToOpen = Uri.decode(fileToOpen);
329-
}
330-
if ("content".equals(scheme)) {
331-
if ("com.android.externalstorage.documents".equals(host)) {
332-
// application "Files" by Google, package="com.android.externalstorage.documents"
333-
if (fileToOpen.matches("^/document/.*:.*$")) {
334-
// decode special uri form: /document/primary:<somebody>
335-
// /document/XXXX-XXXX:<somebody>
336-
String shortcut = fileToOpen.replaceFirst("^/document/(.*):.*$", "$1");
337-
String mountRoot = Engine.getMountRootByShortcut(shortcut);
338-
if (mountRoot != null)
339-
fileToOpen = fileToOpen.replaceFirst("^/document/.*:(.*)$", mountRoot + "/$1");
340-
}
341-
} else if ("com.google.android.apps.nbu.files.provider".equals(host)) {
342-
// application "Files" by Google, package="com.google.android.apps.nbu.files"
343-
if (fileToOpen.startsWith("/1////")) {
344-
// skip "/1///"
345-
fileToOpen = fileToOpen.substring(5);
346-
fileToOpen = Uri.decode(fileToOpen);
347-
} else if (fileToOpen.startsWith("/1/file:///")) {
348-
// skip "/1/file://"
349-
fileToOpen = fileToOpen.substring(10);
350-
fileToOpen = Uri.decode(fileToOpen);
351-
}
352-
}
353-
}
354-
}
355-
if (fileToOpen != null) {
356-
// patch for opening of books from ReLaunch (under Nook Simple Touch)
357-
while (fileToOpen.indexOf("%2F") >= 0) {
358-
fileToOpen = fileToOpen.replace("%2F", "/");
359-
}
360310
log.d("FILE_TO_OPEN = " + fileToOpen);
361311
final String finalFileToOpen = fileToOpen;
362312
loadDocument(fileToOpen, new Runnable() {
@@ -379,12 +329,105 @@ public void onDismiss(DialogInterface dialog) {
379329
}
380330
});
381331
return true;
332+
} else if (null != uri) {
333+
log.d("URI_TO_OPEN = " + uri);
334+
final String uriString = uri.toString();
335+
loadDocumentFromUri(uri, new Runnable() {
336+
@Override
337+
public void run() {
338+
BackgroundThread.instance().postGUI(new Runnable() {
339+
@Override
340+
public void run() {
341+
// if document not loaded show error & then root window
342+
ErrorDialog errDialog = new ErrorDialog(CoolReader.this, CoolReader.this.getString(R.string.error), CoolReader.this.getString(R.string.cant_open_file, uriString));
343+
errDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
344+
@Override
345+
public void onDismiss(DialogInterface dialog) {
346+
showRootWindow();
347+
}
348+
});
349+
errDialog.show();
350+
}
351+
}, 500);
352+
}
353+
});
354+
return true;
382355
} else {
383356
log.d("No file to open");
384357
return false;
385358
}
386359
}
387360

361+
private String filePathFromUri(Uri uri) {
362+
if (null == uri)
363+
return null;
364+
String filePath = null;
365+
String scheme = uri.getScheme();
366+
String host = uri.getHost();
367+
if ("file".equals(scheme)) {
368+
filePath = uri.getPath();
369+
// patch for opening of books from ReLaunch (under Nook Simple Touch)
370+
if (null != filePath) {
371+
if (filePath.contains("%2F"))
372+
filePath = filePath.replace("%2F", "/");
373+
}
374+
} else if ("content".equals(scheme)) {
375+
if (uri.getEncodedPath().contains("%00"))
376+
filePath = uri.getEncodedPath();
377+
else
378+
filePath = uri.getPath();
379+
if (null != filePath) {
380+
// parse uri from system filemanager
381+
if (filePath.contains("%00")) {
382+
// splitter between archive file name and inner file.
383+
filePath = filePath.replace("%00", "@/");
384+
filePath = Uri.decode(filePath);
385+
}
386+
if ("com.android.externalstorage.documents".equals(host)) {
387+
// application "Files" by Google, package="com.android.externalstorage.documents"
388+
if (filePath.matches("^/document/.*:.*$")) {
389+
// decode special uri form: /document/primary:<somebody>
390+
// /document/XXXX-XXXX:<somebody>
391+
String shortcut = filePath.replaceFirst("^/document/(.*):.*$", "$1");
392+
String mountRoot = Engine.getMountRootByShortcut(shortcut);
393+
if (mountRoot != null) {
394+
filePath = filePath.replaceFirst("^/document/.*:(.*)$", mountRoot + "/$1");
395+
}
396+
}
397+
} else if ("com.google.android.apps.nbu.files.provider".equals(host)) {
398+
// application "Files" by Google, package="com.google.android.apps.nbu.files"
399+
if (filePath.startsWith("/1////")) {
400+
// skip "/1///"
401+
filePath = filePath.substring(5);
402+
filePath = Uri.decode(filePath);
403+
} else if (filePath.startsWith("/1/file:///")) {
404+
// skip "/1/file://"
405+
filePath = filePath.substring(10);
406+
filePath = Uri.decode(filePath);
407+
}
408+
} else {
409+
// Try some common conversions...
410+
if (filePath.startsWith("/file%3A%2F%2F")) {
411+
filePath = filePath.substring(14);
412+
filePath = Uri.decode(filePath);
413+
if (filePath.contains("%20")) {
414+
filePath = filePath.replace("%20", " ");
415+
}
416+
}
417+
}
418+
}
419+
}
420+
File file;
421+
int pos = filePath.indexOf("@/");
422+
if (pos > 0)
423+
file = new File(filePath.substring(0, pos));
424+
else
425+
file = new File(filePath);
426+
if (!file.exists())
427+
filePath = null;
428+
return filePath;
429+
}
430+
388431
@Override
389432
protected void onPause() {
390433
super.onPause();
@@ -915,6 +958,22 @@ public void run() {
915958
});
916959
}
917960

961+
public void loadDocumentFromUri(final Uri uri, final Runnable callback) {
962+
runInReader(new Runnable() {
963+
@Override
964+
public void run() {
965+
ContentResolver contentResolver = getContentResolver();
966+
InputStream inputStream = null;
967+
try {
968+
inputStream = contentResolver.openInputStream(uri);
969+
mReaderView.loadDocumentFromStream(inputStream, uri.getPath(), callback);
970+
} catch (FileNotFoundException e) {
971+
callback.run();
972+
}
973+
}
974+
});
975+
}
976+
918977
public void loadDocument(FileInfo item) {
919978
loadDocument(item, null);
920979
}

android/src/org/coolreader/crengine/DocView.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
import android.graphics.Bitmap;
55

6+
import java.io.ByteArrayOutputStream;
7+
import java.io.IOException;
8+
import java.io.InputStream;
9+
610
public class DocView {
711

812
public static final Logger log = L.create("dv");
@@ -159,6 +163,32 @@ public boolean loadDocument(String fileName) {
159163
}
160164
}
161165

166+
/**
167+
* Load document from input stream.
168+
* @param inputStream
169+
* @param contentPath
170+
* @return
171+
*/
172+
public boolean loadDocumentFromStream(InputStream inputStream, String contentPath) {
173+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
174+
byte [] buf = new byte [1024];
175+
int readBytes;
176+
while (true) {
177+
try {
178+
readBytes = inputStream.read(buf);
179+
if (readBytes > 0)
180+
outputStream.write(buf, 0, readBytes);
181+
else
182+
break;
183+
} catch (IOException e) {
184+
break;
185+
}
186+
}
187+
synchronized(mutex) {
188+
return loadDocumentFromMemoryInternal(outputStream.toByteArray(), contentPath);
189+
}
190+
}
191+
162192
/**
163193
* Get settings from native object.
164194
* @return
@@ -411,6 +441,8 @@ public void hilightBookmarks(Bookmark[] bookmarks) {
411441

412442
private native boolean loadDocumentInternal(String fileName);
413443

444+
private native boolean loadDocumentFromMemoryInternal(byte [] buf, String contentPath);
445+
414446
private native java.util.Properties getSettingsInternal();
415447

416448
private native boolean applySettingsInternal(

0 commit comments

Comments
 (0)