summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Lohmaier <lohmaier+LibreOffice@googlemail.com>2015-10-11 01:52:39 +0200
committerTomaž Vajngerl <tomaz.vajngerl@collabora.com>2015-10-15 10:28:28 +0200
commit7f87188e9a35cb8ab06a94ede5504c5d49993766 (patch)
tree9ab65ed9995eb379c94cb23a518ec70cb330e13a
parentmigrate android build to gradle (diff)
downloadcore-7f87188e9a35cb8ab06a94ede5504c5d49993766.tar.gz
core-7f87188e9a35cb8ab06a94ede5504c5d49993766.zip
move extracting assets to Java & use AssetManager to access assets
using AssetsManager in both java as well as native parts allows to handle files both with and without compression transparently Change-Id: If02f1159c498be7ea965fd9c217410722f2dca1f (cherry picked from commit 143fb0a4b5d4ab69d4928299d8112ab95d99870a)
-rw-r--r--android/Bootstrap/src/org/libreoffice/kit/LibreOfficeKit.java8
-rw-r--r--android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java139
-rw-r--r--include/osl/detail/android-bootstrap.h5
-rw-r--r--sal/android/libreofficekit-jni.c19
-rw-r--r--sal/android/lo-bootstrap.c182
-rw-r--r--sal/osl/unx/file.cxx29
6 files changed, 149 insertions, 233 deletions
diff --git a/android/Bootstrap/src/org/libreoffice/kit/LibreOfficeKit.java b/android/Bootstrap/src/org/libreoffice/kit/LibreOfficeKit.java
index 431c384726d0..b2fb5e12053a 100644
--- a/android/Bootstrap/src/org/libreoffice/kit/LibreOfficeKit.java
+++ b/android/Bootstrap/src/org/libreoffice/kit/LibreOfficeKit.java
@@ -11,6 +11,7 @@ package org.libreoffice.kit;
import android.app.Activity;
import android.content.pm.ApplicationInfo;
+import android.content.res.AssetManager;
import android.util.Log;
import java.io.File;
@@ -24,6 +25,7 @@ import java.nio.ByteBuffer;
public final class LibreOfficeKit
{
private static String LOGTAG = LibreOfficeKit.class.getSimpleName();
+ private static AssetManager mgr;
// private constructor because instantiating would be meaningless
private LibreOfficeKit() {
@@ -34,7 +36,7 @@ public final class LibreOfficeKit
}
// Trigger initialization on the JNI - LOKit side.
- private static native boolean initializeNative(String dataDir, String cacheDir, String apkFile);
+ private static native boolean initializeNative(String dataDir, String cacheDir, String apkFile, AssetManager mgr);
public static native ByteBuffer getLibreOfficeKitHandle();
@@ -55,6 +57,8 @@ public final class LibreOfficeKit
return;
}
+ mgr = activity.getResources().getAssets();
+
ApplicationInfo applicationInfo = activity.getApplicationInfo();
String dataDir = applicationInfo.dataDir;
Log.i(LOGTAG, String.format("Initializing LibreOfficeKit, dataDir=%s\n", dataDir));
@@ -83,7 +87,7 @@ public final class LibreOfficeKit
// TMPDIR is used by osl_getTempDirURL()
putenv("TMPDIR=" + cacheDir);
- if (!initializeNative(dataDir, cacheDir, apkFile)) {
+ if (!initializeNative(dataDir, cacheDir, apkFile, mgr)) {
Log.e(LOGTAG, "Initialize native failed!");
return;
}
diff --git a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java
index 324423c30aa3..eb566073f2a7 100644
--- a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java
+++ b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java
@@ -6,6 +6,9 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -33,9 +36,11 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.List;
@@ -47,6 +52,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity {
private static final String LOGTAG = "LibreOfficeMainActivity";
private static final String DEFAULT_DOC_PATH = "/assets/example.odt";
private static final String ENABLE_EXPERIMENTAL_PREFS_KEY = "ENABLE_EXPERIMENTAL";
+ private static final String ASSETS_EXTRACTED_PREFS_KEY = "ASSETS_EXTRACTED";
public static LibreOfficeMainActivity mAppContext;
@@ -147,9 +153,15 @@ public class LibreOfficeMainActivity extends AppCompatActivity {
mAppContext = this;
super.onCreate(savedInstanceState);
- mEnableEditing = PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
- .getBoolean(ENABLE_EXPERIMENTAL_PREFS_KEY, false);
+ SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ mEnableEditing = sPrefs.getBoolean(ENABLE_EXPERIMENTAL_PREFS_KEY, false);
+
+ if (sPrefs.getInt(ASSETS_EXTRACTED_PREFS_KEY, 0) != BuildConfig.VERSION_CODE) {
+ if(copyFromAssets(getAssets(), "unpack", getApplicationInfo().dataDir)) {
+ sPrefs.edit().putInt(ASSETS_EXTRACTED_PREFS_KEY, BuildConfig.VERSION_CODE).apply();
+ }
+ }
mMainHandler = new Handler();
setContentView(R.layout.activity_main);
@@ -165,6 +177,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity {
Log.d(LOGTAG, "SCHEME_CONTENT: getPath(): " + getIntent().getData().getPath());
} else {
// TODO: can't open the file
+ Log.e(LOGTAG, "couldn't create temporary file from "+getIntent().getData());
}
} else if (getIntent().getData().getScheme().equals(ContentResolver.SCHEME_FILE)) {
mInputFile = new File(getIntent().getData().getPath());
@@ -217,38 +230,42 @@ public class LibreOfficeMainActivity extends AppCompatActivity {
private boolean copyFileToTemp() {
ContentResolver contentResolver = getContentResolver();
- InputStream inputStream = null;
+ FileChannel inputChannel = null;
+ FileChannel outputChannel = null;
+ // CSV files need a .csv suffix to be opened in Calc.
+ String suffix = null;
+ String intentType = getIntent().getType();
+ // K-9 mail uses the first, GMail uses the second variant.
+ if ("text/comma-separated-values".equals(intentType) || "text/csv".equals(intentType))
+ suffix = ".csv";
+
try {
- inputStream = contentResolver.openInputStream(getIntent().getData());
-
- // CSV files need a .csv suffix to be opened in Calc.
- String suffix = null;
- String intentType = getIntent().getType();
- // K-9 mail uses the first, GMail uses the second variant.
- if ("text/comma-separated-values".equals(intentType) || "text/csv".equals(intentType))
- suffix = ".csv";
- mTempFile = File.createTempFile("LibreOffice", suffix, this.getCacheDir());
-
- OutputStream outputStream = new FileOutputStream(mTempFile);
- byte[] buffer = new byte[4096];
- int len = 0;
- while ((len = inputStream.read(buffer)) != -1) {
- outputStream.write(buffer, 0, len);
+ try {
+ AssetFileDescriptor assetFD = contentResolver.openAssetFileDescriptor(getIntent().getData(), "r");
+ if (assetFD == null) {
+ Log.e(LOGTAG, "couldn't create assetfiledescriptor from "+getIntent().getDataString());
+ return false;
+ }
+ inputChannel = assetFD.createInputStream().getChannel();
+ mTempFile = File.createTempFile("LibreOffice", suffix, this.getCacheDir());
+
+ outputChannel = new FileOutputStream(mTempFile).getChannel();
+ long bytesTransferred = 0;
+ // might not copy all at once, so make sure everything gets copied....
+ while (bytesTransferred < inputChannel.size()) {
+ bytesTransferred += outputChannel.transferFrom(inputChannel, bytesTransferred, inputChannel.size());
+ }
+ Log.e(LOGTAG, "Success copying "+bytesTransferred+ " bytes");
+ return true;
+ } finally {
+ if (inputChannel != null) inputChannel.close();
+ if (outputChannel != null) outputChannel.close();
}
- inputStream.close();
- outputStream.close();
- return true;
} catch (FileNotFoundException e) {
+ return false;
} catch (IOException e) {
- } finally {
- if (inputStream != null) {
- try {
- inputStream.close();
- } catch (IOException e) {
- }
- }
+ return false;
}
- return false;
}
/**
@@ -362,6 +379,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity {
if (isFinishing()) { // Not an orientation change
if (mTempFile != null) {
+ //noinspection ResultOfMethodCallIgnored
mTempFile.delete();
}
}
@@ -423,8 +441,6 @@ public class LibreOfficeMainActivity extends AppCompatActivity {
* Hides software keyboard.
*/
private void hideSoftKeyboardDirect() {
- LayerView layerView = (LayerView) findViewById(R.id.layer_view);
-
if (getCurrentFocus() != null) {
InputMethodManager inputMethodManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
@@ -471,6 +487,63 @@ public class LibreOfficeMainActivity extends AppCompatActivity {
mDrawerLayout.closeDrawer(mDrawerList);
}
}
+
+ private static boolean copyFromAssets(AssetManager assetManager,
+ String fromAssetPath, String targetDir) {
+ try {
+ String[] files = assetManager.list(fromAssetPath);
+
+ boolean res = true;
+ for (String file : files) {
+ String[] dirOrFile = assetManager.list(fromAssetPath+"/"+file);
+ if ( dirOrFile.length == 0) {
+ //noinspection ResultOfMethodCallIgnored
+ new File(targetDir).mkdirs();
+ res &= copyAsset(assetManager,
+ fromAssetPath + "/" + file,
+ targetDir + "/" + file);
+ } else
+ res &= copyFromAssets(assetManager,
+ fromAssetPath + "/" + file,
+ targetDir + "/" + file);
+ }
+ return res;
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(LOGTAG, "copyFromAssets failed: " + e.getMessage());
+ return false;
+ }
+ }
+
+ private static boolean copyAsset(AssetManager assetManager, String fromAssetPath, String toPath) {
+ ReadableByteChannel source = null;
+ FileChannel dest = null;
+ try {
+ try {
+ source = Channels.newChannel(assetManager.open(fromAssetPath));
+ dest = new FileOutputStream(toPath).getChannel();
+ long bytesTransferred = 0;
+ // might not copy all at once, so make sure everything gets copied....
+ ByteBuffer buffer = ByteBuffer.allocate(4096);
+ while (source.read(buffer)>0) {
+ buffer.flip();
+ bytesTransferred += dest.write(buffer);
+ buffer.clear();
+ }
+ Log.v(LOGTAG, "Success copying "+fromAssetPath+" to "+toPath + " bytes: "+bytesTransferred);
+ return true;
+ } finally {
+ if (dest != null) dest.close();
+ if (source != null) source.close();
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(LOGTAG, "file " + fromAssetPath + " not found! " + e.getMessage());
+ return false;
+ } catch (IOException e) {
+ Log.e(LOGTAG, "failed to copy file " + fromAssetPath + " from assets to " + toPath + " - " + e.getMessage());
+ return false;
+ }
+ }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/osl/detail/android-bootstrap.h b/include/osl/detail/android-bootstrap.h
index b18531bb65e2..5a5c7fcbd82d 100644
--- a/include/osl/detail/android-bootstrap.h
+++ b/include/osl/detail/android-bootstrap.h
@@ -20,6 +20,7 @@ extern "C" {
#endif
#include <osl/detail/component-mapping.h>
+#include <android/asset_manager.h>
typedef struct lo_apk_dir lo_apk_dir;
@@ -38,12 +39,10 @@ JavaVM *lo_get_javavm(void);
const char *lo_get_app_data_dir(void);
-#define UNPACK_TREE "/assets/unpack"
-#define UNPACK_TREE_GZ "/assets/gz.unpack"
+AAssetManager *lo_get_native_assetmgr(void);
int setup_cdir(void);
int setup_assets_tree(void);
-void extract_files(const char *root, const char *prefix, int gzipped);
#ifdef __cplusplus
}
diff --git a/sal/android/libreofficekit-jni.c b/sal/android/libreofficekit-jni.c
index 41fa97e8273d..c5f53c92bcd5 100644
--- a/sal/android/libreofficekit-jni.c
+++ b/sal/android/libreofficekit-jni.c
@@ -21,6 +21,8 @@
#include <jni.h>
#include <android/log.h>
+#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
#include <osl/detail/android-bootstrap.h>
@@ -34,6 +36,7 @@ extern const char* data_dir;
extern const char* cache_dir;
extern void* apk_file;
extern int apk_file_size;
+AAssetManager* native_asset_manager;
extern void Java_org_libreoffice_android_Bootstrap_putenv(JNIEnv* env, jobject clazz, jstring string);
extern void Java_org_libreoffice_android_Bootstrap_redirect_1stdio(JNIEnv* env, jobject clazz, jboolean state);
@@ -63,7 +66,7 @@ void Java_org_libreoffice_kit_LibreOfficeKit_redirectStdio
__attribute__ ((visibility("default")))
jboolean Java_org_libreoffice_kit_LibreOfficeKit_initializeNative
(JNIEnv* env, jobject clazz,
- jstring dataDir, jstring cacheDir, jstring apkFile)
+ jstring dataDir, jstring cacheDir, jstring apkFile, jobject assetManager)
{
struct stat st;
int fd;
@@ -76,6 +79,8 @@ jboolean Java_org_libreoffice_kit_LibreOfficeKit_initializeNative
(void) clazz;
+ native_asset_manager = AAssetManager_fromJava(env, assetManager);
+
dataDirPath = (*env)->GetStringUTFChars(env, dataDir, NULL);
data_dir = strdup(dataDirPath);
(*env)->ReleaseStringUTFChars(env, dataDir, dataDirPath);
@@ -122,10 +127,6 @@ jboolean Java_org_libreoffice_kit_LibreOfficeKit_initializeNative
return JNI_FALSE;
}
- // Extract files from the .apk that can't be used mmapped directly from it
- extract_files(UNPACK_TREE, UNPACK_TREE, 0);
- extract_files(UNPACK_TREE_GZ, UNPACK_TREE_GZ, 1);
-
// LibreOfficeKit expects a path to the program/ directory
free(full_program_dir);
data_dir_len = strlen(data_dir);
@@ -160,4 +161,12 @@ jobject Java_org_libreoffice_kit_LibreOfficeKit_getLibreOfficeKitHandle
return (*env)->NewDirectByteBuffer(env, (void*) aOffice, sizeof(LibreOfficeKit));
}
+__attribute__ ((visibility("default")))
+AAssetManager *
+lo_get_native_assetmgr(void)
+{
+ return native_asset_manager;
+}
+
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/android/lo-bootstrap.c b/sal/android/lo-bootstrap.c
index 5c47b8f6bd2f..7a2da48eb93b 100644
--- a/sal/android/lo-bootstrap.c
+++ b/sal/android/lo-bootstrap.c
@@ -311,10 +311,6 @@ Java_org_libreoffice_android_Bootstrap_setup__Ljava_lang_String_2Ljava_lang_Stri
if (!setup_assets_tree())
return JNI_FALSE;
- // Extract files from the .apk that can't be used mmapped directly from it
- extract_files(UNPACK_TREE, UNPACK_TREE, 0);
- extract_files(UNPACK_TREE_GZ, UNPACK_TREE_GZ, 1);
-
return JNI_TRUE;
}
@@ -635,184 +631,6 @@ lo_apk_lstat(const char *path,
return -1;
}
-static int
-mkdir_p(const char *dirname)
-{
- char *p = malloc(strlen(dirname) + 1);
- const char *q = dirname + 1;
- const char *slash;
-
- do {
- slash = strchr(q, '/');
- if (slash == NULL)
- slash = q + strlen(q);
- memcpy(p, dirname, slash-dirname);
- p[slash-dirname] = '\0';
- if (mkdir(p, 0700) == -1 && errno != EEXIST) {
- LOGE("mkdir_p: Could not create %s: %s", p, strerror(errno));
- free(p);
- return 0;
- }
- if (*slash)
- q = slash + 1;
- } while (*slash);
-
- free(p);
- return 1;
-}
-
-static int
-extract_gzipped(const char *filename,
- const char *apkentry,
- int size,
- FILE *f)
-{
- gzFile gzfd;
- int gzerrno;
- int nbytes;
- char buf[5000];
- int total = 0;
- char *tmpname;
- FILE *tmp;
-
- tmpname = malloc(strlen(cache_dir) + strlen("/tmp.gz") + 1);
- strcpy(tmpname, cache_dir);
- strcat(tmpname, "/tmp.gz");
-
- tmp = fopen(tmpname, "w+");
- unlink(tmpname);
-
- if (tmp == NULL) {
- LOGE("extract_gzipped: could not create %s: %s", tmpname, strerror(errno));
- free(tmpname);
- return 0;
- }
-
- if (fwrite(apkentry, size, 1, tmp) != 1) {
- LOGE("extract_gzipped: could not write gzipped entry to %s: %s", tmpname, strerror(errno));
- fclose(tmp);
- free(tmpname);
- return 0;
- }
-
- free(tmpname);
- rewind(tmp);
-
- gzfd = gzdopen(fileno(tmp), "rb");
- if (gzfd == NULL) {
- LOGE("extract_gzipped: gzdopen failed");
- fclose(tmp);
- return 0;
- }
-
- while ((nbytes = gzread(gzfd, buf, sizeof(buf))) > 0) {
- fwrite(buf, nbytes, 1, f);
- total += nbytes;
- }
- if (nbytes == -1) {
- LOGE("extract_gzipped: Could not gzread from %s: %s", filename, gzerror(gzfd, &gzerrno));
- return total;
- }
- if (gzclose(gzfd) == -1) {
- LOGE("extract_gzipped: gzclose failed");
- return total;
- }
-
- return total;
-}
-
-void
-extract_files(const char *root,
- const char *prefix,
- int gzipped)
-{
- lo_apk_dir *tree = lo_apk_opendir(prefix);
- struct dirent *dent;
-
- if (tree == NULL)
- return;
-
- while ((dent = lo_apk_readdir(tree)) != NULL) {
- if (strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0)
- continue;
-
- if (dent->d_type == DT_DIR) {
- char *subdir = malloc(strlen(prefix) + 1 + strlen(dent->d_name) + 1);
- strcpy(subdir, prefix);
- strcat(subdir, "/");
- strcat(subdir, dent->d_name);
- extract_files(root, subdir, gzipped);
- free(subdir);
- } else {
- char *filename;
- char *newfilename;
- const char *apkentry;
- size_t size;
- struct stat st;
- FILE *f;
-
- filename = malloc(strlen(prefix) + 1 + strlen(dent->d_name) + 1);
- strcpy(filename, prefix);
- strcat(filename, "/");
- strcat(filename, dent->d_name);
-
- apkentry = lo_apkentry(filename, &size);
- if (apkentry == NULL) {
- LOGE("extract_files: Could not find %s in .apk", filename);
- free(filename);
- continue;
- }
-
- newfilename = malloc(strlen(data_dir) + 1 + strlen(prefix) - strlen(root) + strlen(dent->d_name) + 1);
- strcpy(newfilename, data_dir);
- strcat(newfilename, "/");
- strcat(newfilename, prefix + strlen(root) + 1);
-
- if (!mkdir_p(newfilename)) {
- free(filename);
- free(newfilename);
- continue;
- }
-
- strcat(newfilename, "/");
- strcat(newfilename, dent->d_name);
-
- if (stat(newfilename, &st) == 0 &&
- (gzipped || st.st_size == (long long) size)) {
- free(filename);
- free(newfilename);
- continue;
- }
-
- f = fopen(newfilename, "w");
- if (f == NULL) {
- LOGE("extract_files: Could not open %s for writing: %s", newfilename, strerror(errno));
- free(filename);
- free(newfilename);
- continue;
- }
-
- if (!gzipped) {
- if (fwrite(apkentry, size, 1, f) != 1) {
- LOGE("extract_files: Could not write %lld bytes to %s: %s", (long long) size, newfilename, strerror(errno));
- } else {
- LOGI("extract_files: Copied %s to %s: %lld bytes", filename, newfilename, (long long) size);
- }
- } else {
- size = extract_gzipped(filename, apkentry, size, f);
- LOGI("extract_files: Decompressed %s to %s: %lld bytes", filename, newfilename, (long long) size);
- }
-
- fclose(f);
-
- free(filename);
- free(newfilename);
- }
- }
- lo_apk_closedir(tree);
-}
-
/* Android's JNI works only to libraries loaded through Java's
* System.loadLibrary(), it seems. But now with just one big app-specific .so
* on Android, that would not be a problem, but for historical reasons, we
diff --git a/sal/osl/unx/file.cxx b/sal/osl/unx/file.cxx
index 73612a32d273..a4664c924a22 100644
--- a/sal/osl/unx/file.cxx
+++ b/sal/osl/unx/file.cxx
@@ -56,6 +56,8 @@
#ifdef ANDROID
#include <osl/detail/android-bootstrap.h>
+#include <android/log.h>
+#include <android/asset_manager.h>
#endif
/*******************************************************************
@@ -829,6 +831,21 @@ openFilePath( const char *cpFilePath, oslFileHandle* pHandle, sal_uInt32 uFlags,
*/
if (strncmp (cpFilePath, "/assets/", sizeof ("/assets/") - 1) == 0)
{
+ void* address;
+ size_t size;
+ AAssetManager* mgr = lo_get_native_assetmgr();
+ AAsset* asset = AAssetManager_open(mgr, cpFilePath + sizeof("/assets/")-1, AASSET_MODE_BUFFER);
+ if (NULL == asset) {
+ address = NULL;
+ errno = ENOENT;
+ __android_log_print(ANDROID_LOG_ERROR,"libo:sal/osl/unx/file", "failed to open %s", cpFilePath);
+ return osl_File_E_NOENT;
+ } else {
+ size = AAsset_getLength(asset);
+ address = malloc (sizeof(char)*size);
+ AAsset_read (asset,address,size);
+ AAsset_close(asset);
+ }
if (uFlags & osl_File_OpenFlag_Write)
{
// It seems to work better to silently "open" it read-only
@@ -836,15 +853,7 @@ openFilePath( const char *cpFilePath, oslFileHandle* pHandle, sal_uInt32 uFlags,
// loading a document from /assets fails with that idiotic
// "General Error" dialog...
}
- void *address;
- size_t size;
- address = lo_apkentry(cpFilePath, &size);
SAL_INFO("sal.file", "osl_openFile(" << cpFilePath << ") => " << address);
- if (address == NULL)
- {
- errno = ENOENT;
- return osl_File_E_NOENT;
- }
return openMemoryAsFile(address, size, pHandle, cpFilePath);
}
#endif
@@ -1042,6 +1051,10 @@ SAL_CALL osl_closeFile( oslFileHandle Handle )
if (pImpl->m_kind == FileHandle_Impl::KIND_MEM)
{
+#ifdef ANDROID
+ free(pImpl->m_buffer);
+ pImpl->m_buffer = NULL;
+#endif
delete pImpl;
return osl_File_E_None;
}