2019-07-11 11:29:13 +02:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
|
|
/*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package org.libreoffice.androidlib;
|
|
|
|
|
|
|
|
import android.Manifest;
|
2020-03-13 19:21:14 +01:00
|
|
|
import android.app.AlertDialog;
|
2019-06-17 02:30:00 +02:00
|
|
|
import android.content.ClipData;
|
|
|
|
import android.content.ClipDescription;
|
|
|
|
import android.content.ClipboardManager;
|
2019-05-09 22:57:16 +02:00
|
|
|
import android.content.ActivityNotFoundException;
|
2019-07-11 11:29:13 +02:00
|
|
|
import android.content.ContentResolver;
|
2020-07-15 21:29:23 +02:00
|
|
|
import android.content.Context;
|
2020-03-13 19:21:14 +01:00
|
|
|
import android.content.DialogInterface;
|
2019-07-11 11:29:13 +02:00
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.SharedPreferences;
|
|
|
|
import android.content.pm.ApplicationInfo;
|
|
|
|
import android.content.pm.PackageManager;
|
|
|
|
import android.content.res.AssetFileDescriptor;
|
|
|
|
import android.content.res.AssetManager;
|
|
|
|
import android.net.Uri;
|
2020-02-12 17:39:50 +01:00
|
|
|
import android.os.AsyncTask;
|
2019-07-11 11:29:13 +02:00
|
|
|
import android.os.Build;
|
|
|
|
import android.os.Bundle;
|
2020-02-11 15:39:03 +01:00
|
|
|
import android.os.Environment;
|
2019-07-11 11:29:13 +02:00
|
|
|
import android.os.Handler;
|
2019-06-17 02:30:00 +02:00
|
|
|
import android.os.Looper;
|
2019-07-11 11:29:13 +02:00
|
|
|
import android.preference.PreferenceManager;
|
|
|
|
import android.print.PrintAttributes;
|
|
|
|
import android.print.PrintDocumentAdapter;
|
|
|
|
import android.print.PrintManager;
|
2020-02-11 15:39:03 +01:00
|
|
|
import android.provider.DocumentsContract;
|
2019-07-11 11:29:13 +02:00
|
|
|
import android.util.Log;
|
2020-02-06 14:27:01 +01:00
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.View;
|
2020-02-12 21:49:17 +01:00
|
|
|
import android.view.ViewGroup;
|
2019-07-28 01:43:26 +02:00
|
|
|
import android.view.WindowManager;
|
2019-07-11 11:29:13 +02:00
|
|
|
import android.webkit.JavascriptInterface;
|
2019-05-09 22:57:16 +02:00
|
|
|
import android.webkit.ValueCallback;
|
|
|
|
import android.webkit.WebChromeClient;
|
2019-07-11 11:29:13 +02:00
|
|
|
import android.webkit.WebSettings;
|
|
|
|
import android.webkit.WebView;
|
2020-03-13 19:21:14 +01:00
|
|
|
import android.widget.RatingBar;
|
2020-02-06 14:27:01 +01:00
|
|
|
import android.widget.TextView;
|
2019-07-11 11:29:13 +02:00
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileInputStream;
|
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStream;
|
2020-03-17 22:06:50 +01:00
|
|
|
import java.io.BufferedWriter;
|
2019-07-11 11:29:13 +02:00
|
|
|
import java.net.URI;
|
|
|
|
import java.net.URISyntaxException;
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.nio.channels.Channels;
|
|
|
|
import java.nio.channels.FileChannel;
|
|
|
|
import java.nio.channels.ReadableByteChannel;
|
2020-01-15 10:32:42 +01:00
|
|
|
import java.nio.charset.Charset;
|
2020-02-11 15:39:03 +01:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Map;
|
2019-07-11 11:29:13 +02:00
|
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
|
|
import androidx.core.app.ActivityCompat;
|
|
|
|
import androidx.core.content.ContextCompat;
|
2019-12-10 16:12:52 +01:00
|
|
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
2019-07-11 11:29:13 +02:00
|
|
|
|
2020-01-15 10:32:42 +01:00
|
|
|
import org.libreoffice.androidlib.lok.LokClipboardData;
|
|
|
|
import org.libreoffice.androidlib.lok.LokClipboardEntry;
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
public class LOActivity extends AppCompatActivity {
|
|
|
|
final static String TAG = "LOActivity";
|
|
|
|
|
2019-10-29 20:37:50 +01:00
|
|
|
private static final String ASSETS_EXTRACTED_GIT_COMMIT = "ASSETS_EXTRACTED_GIT_COMMIT";
|
2019-07-23 12:38:41 +02:00
|
|
|
private static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 777;
|
2019-07-11 11:29:13 +02:00
|
|
|
private static final String KEY_ENABLE_SHOW_DEBUG_INFO = "ENABLE_SHOW_DEBUG_INFO";
|
|
|
|
|
|
|
|
private static final String KEY_PROVIDER_ID = "providerID";
|
|
|
|
private static final String KEY_DOCUMENT_URI = "documentUri";
|
|
|
|
private static final String KEY_IS_EDITABLE = "isEditable";
|
|
|
|
private static final String KEY_INTENT_URI = "intentUri";
|
2020-01-15 10:32:42 +01:00
|
|
|
private static final String CLIPBOARD_FILE_PATH = "LibreofficeClipboardFile.data";
|
|
|
|
private static final String CLIPBOARD_LOOL_SIGNATURE = "lool-clip-magic-4a22437e49a8-";
|
2019-07-11 11:29:13 +02:00
|
|
|
private File mTempFile = null;
|
|
|
|
|
|
|
|
private int providerId;
|
|
|
|
|
2019-12-18 18:10:04 +01:00
|
|
|
/// Unique number identifying this app + document.
|
|
|
|
private long loadDocumentMillis = 0;
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
@Nullable
|
|
|
|
private URI documentUri;
|
|
|
|
|
|
|
|
private String urlToLoad;
|
2020-07-13 18:21:28 +02:00
|
|
|
private WebView mWebView = null;
|
2019-07-11 11:29:13 +02:00
|
|
|
private SharedPreferences sPrefs;
|
2020-03-04 11:33:17 +01:00
|
|
|
private Handler mMainHandler = null;
|
2020-03-13 19:21:14 +01:00
|
|
|
private RateAppController rateAppController;
|
2019-07-11 11:29:13 +02:00
|
|
|
|
|
|
|
private boolean isDocEditable = false;
|
|
|
|
private boolean isDocDebuggable = BuildConfig.DEBUG;
|
2019-07-23 14:17:34 +02:00
|
|
|
private boolean documentLoaded = false;
|
2019-07-11 11:29:13 +02:00
|
|
|
|
2019-06-17 02:30:00 +02:00
|
|
|
private ClipboardManager clipboardManager;
|
|
|
|
private ClipData clipData;
|
|
|
|
private Thread nativeMsgThread;
|
|
|
|
private Handler nativeHandler;
|
|
|
|
private Looper nativeLooper;
|
2020-02-12 17:39:50 +01:00
|
|
|
private Bundle savedInstanceState;
|
2019-06-17 02:30:00 +02:00
|
|
|
|
2020-02-28 18:30:52 +01:00
|
|
|
private ProgressDialog mProgressDialog = null;
|
|
|
|
|
2020-01-14 16:49:36 +01:00
|
|
|
/** In case the mobile-wizard is visible, we have to intercept the Android's Back button. */
|
|
|
|
private boolean mMobileWizardVisible = false;
|
2020-02-12 18:53:34 +01:00
|
|
|
private boolean mIsEditModeActive = false;
|
2020-01-14 16:49:36 +01:00
|
|
|
|
2019-05-09 22:57:16 +02:00
|
|
|
private ValueCallback<Uri[]> valueCallback;
|
2020-02-11 15:39:03 +01:00
|
|
|
|
|
|
|
public static final int REQUEST_SELECT_IMAGE_FILE = 500;
|
|
|
|
public static final int REQUEST_SAVEAS_PDF = 501;
|
|
|
|
public static final int REQUEST_SAVEAS_RTF = 502;
|
|
|
|
public static final int REQUEST_SAVEAS_ODT = 503;
|
|
|
|
public static final int REQUEST_SAVEAS_ODP = 504;
|
|
|
|
public static final int REQUEST_SAVEAS_ODS = 505;
|
|
|
|
public static final int REQUEST_SAVEAS_DOCX = 506;
|
|
|
|
public static final int REQUEST_SAVEAS_PPTX = 507;
|
|
|
|
public static final int REQUEST_SAVEAS_XLSX = 508;
|
|
|
|
public static final int REQUEST_SAVEAS_DOC = 509;
|
|
|
|
public static final int REQUEST_SAVEAS_PPT = 510;
|
|
|
|
public static final int REQUEST_SAVEAS_XLS = 511;
|
2020-03-17 12:41:41 +01:00
|
|
|
public static final int REQUEST_SAVEAS_EPUB = 512;
|
2019-05-09 22:57:16 +02:00
|
|
|
|
2019-12-10 16:12:52 +01:00
|
|
|
/** Broadcasting event for passing info back to the shell. */
|
|
|
|
public static final String LO_ACTIVITY_BROADCAST = "LOActivityBroadcast";
|
|
|
|
|
|
|
|
/** Event description for passing info back to the shell. */
|
|
|
|
public static final String LO_ACTION_EVENT = "LOEvent";
|
|
|
|
|
|
|
|
/** Data description for passing info back to the shell. */
|
|
|
|
public static final String LO_ACTION_DATA = "LOData";
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
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(TAG, "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(TAG, "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(TAG, "file " + fromAssetPath + " not found! " + e.getMessage());
|
|
|
|
return false;
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.e(TAG, "failed to copy file " + fromAssetPath + " from assets to " + toPath + " - " + e.getMessage());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 11:33:17 +01:00
|
|
|
private Handler getMainHandler() {
|
|
|
|
if (mMainHandler == null) {
|
|
|
|
mMainHandler = new Handler(getMainLooper());
|
|
|
|
}
|
|
|
|
return mMainHandler;
|
|
|
|
}
|
|
|
|
|
2020-07-15 21:29:23 +02:00
|
|
|
/** True if the App is running under ChromeOS. */
|
|
|
|
public static boolean isChromeOS(Context context) {
|
|
|
|
return context.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
|
|
|
|
}
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
@Override
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
2020-02-12 17:39:50 +01:00
|
|
|
this.savedInstanceState = savedInstanceState;
|
2019-07-28 01:43:26 +02:00
|
|
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
2019-07-11 11:29:13 +02:00
|
|
|
sPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
2020-02-28 18:30:52 +01:00
|
|
|
|
2019-07-15 13:57:18 +02:00
|
|
|
setContentView(R.layout.lolib_activity_main);
|
2020-02-28 18:30:52 +01:00
|
|
|
mProgressDialog = new ProgressDialog(this);
|
2020-03-13 19:21:14 +01:00
|
|
|
if (BuildConfig.GOOGLE_PLAY_ENABLED)
|
|
|
|
this.rateAppController = new RateAppController(this);
|
|
|
|
else
|
|
|
|
this.rateAppController = null;
|
2020-02-28 18:30:52 +01:00
|
|
|
|
2020-02-12 17:39:50 +01:00
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:39:38 +01:00
|
|
|
/** Initialize the app - copy the assets and create the UI. */
|
2020-02-12 17:39:50 +01:00
|
|
|
private void init() {
|
2020-02-21 13:39:38 +01:00
|
|
|
if (sPrefs.getString(ASSETS_EXTRACTED_GIT_COMMIT, "").equals(BuildConfig.GIT_COMMIT)) {
|
|
|
|
// all is fine, we have already copied the assets
|
|
|
|
initUI();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-28 18:30:52 +01:00
|
|
|
mProgressDialog.indeterminate(R.string.preparing_for_the_first_start_after_an_update);
|
2020-02-21 13:39:38 +01:00
|
|
|
|
2020-02-12 17:39:50 +01:00
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... voids) {
|
2020-02-21 13:39:38 +01:00
|
|
|
// copy the new assets
|
2020-07-01 21:05:42 +02:00
|
|
|
if (copyFromAssets(getAssets(), "unpack", getApplicationInfo().dataDir)) {
|
2020-02-21 13:39:38 +01:00
|
|
|
sPrefs.edit().putString(ASSETS_EXTRACTED_GIT_COMMIT, BuildConfig.GIT_COMMIT).apply();
|
|
|
|
}
|
2020-02-12 17:39:50 +01:00
|
|
|
return null;
|
|
|
|
}
|
2019-07-11 11:29:13 +02:00
|
|
|
|
2020-02-12 17:39:50 +01:00
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Void aVoid) {
|
|
|
|
initUI();
|
|
|
|
}
|
|
|
|
}.execute();
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:39:38 +01:00
|
|
|
/** Actual initialization of the UI. */
|
2020-02-12 17:39:50 +01:00
|
|
|
private void initUI() {
|
2019-07-11 11:29:13 +02:00
|
|
|
isDocDebuggable = sPrefs.getBoolean(KEY_ENABLE_SHOW_DEBUG_INFO, false) && BuildConfig.DEBUG;
|
|
|
|
|
|
|
|
if (getIntent().getData() != null) {
|
|
|
|
|
|
|
|
if (getIntent().getData().getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
|
2020-07-17 09:56:25 +02:00
|
|
|
isDocEditable = true;
|
|
|
|
|
|
|
|
// is it read-only?
|
|
|
|
if ((getIntent().getFlags() & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
|
|
|
|
isDocEditable = false;
|
2020-07-17 16:02:46 +02:00
|
|
|
Log.d(TAG, "Disabled editing: Read-only");
|
2020-07-17 09:56:25 +02:00
|
|
|
Toast.makeText(this, getResources().getString(R.string.temp_file_saving_disabled), Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
2020-07-03 23:07:47 +02:00
|
|
|
|
|
|
|
// turns out that on ChromeOS, it is not possible to save back
|
|
|
|
// to Google Drive; detect it already here to avoid disappointment
|
2020-07-17 09:56:25 +02:00
|
|
|
// also the volumeprovider does not work for saving back,
|
|
|
|
// which is much more serious :-(
|
|
|
|
if (isDocEditable && (getIntent().getData().toString().startsWith("content://org.chromium.arc.chromecontentprovider/externalfile") ||
|
|
|
|
getIntent().getData().toString().startsWith("content://org.chromium.arc.volumeprovider/"))) {
|
2020-07-03 23:07:47 +02:00
|
|
|
isDocEditable = false;
|
2020-07-17 16:02:46 +02:00
|
|
|
Log.d(TAG, "Disabled editing: Chrome OS unsupported content providers");
|
2020-07-17 09:56:25 +02:00
|
|
|
Toast.makeText(this, getResources().getString(R.string.file_chromeos_read_only), Toast.LENGTH_LONG).show();
|
|
|
|
}
|
2020-02-13 21:21:23 +01:00
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
if (copyFileToTemp() && mTempFile != null) {
|
|
|
|
documentUri = mTempFile.toURI();
|
|
|
|
urlToLoad = documentUri.toString();
|
|
|
|
Log.d(TAG, "SCHEME_CONTENT: getPath(): " + getIntent().getData().getPath());
|
|
|
|
} else {
|
|
|
|
// TODO: can't open the file
|
|
|
|
Log.e(TAG, "couldn't create temporary file from " + getIntent().getData());
|
|
|
|
}
|
|
|
|
} else if (getIntent().getData().getScheme().equals(ContentResolver.SCHEME_FILE)) {
|
|
|
|
isDocEditable = true;
|
2019-12-11 12:19:28 +01:00
|
|
|
urlToLoad = getIntent().getData().toString();
|
2019-07-11 11:29:13 +02:00
|
|
|
Log.d(TAG, "SCHEME_FILE: getPath(): " + getIntent().getData().getPath());
|
|
|
|
// Gather data to rebuild IFile object later
|
|
|
|
providerId = getIntent().getIntExtra(
|
|
|
|
"org.libreoffice.document_provider_id", 0);
|
|
|
|
documentUri = (URI) getIntent().getSerializableExtra(
|
|
|
|
"org.libreoffice.document_uri");
|
|
|
|
}
|
|
|
|
} else if (savedInstanceState != null) {
|
|
|
|
getIntent().setAction(Intent.ACTION_VIEW)
|
|
|
|
.setData(Uri.parse(savedInstanceState.getString(KEY_INTENT_URI)));
|
|
|
|
urlToLoad = getIntent().getData().toString();
|
|
|
|
providerId = savedInstanceState.getInt(KEY_PROVIDER_ID);
|
|
|
|
if (savedInstanceState.getString(KEY_DOCUMENT_URI) != null) {
|
|
|
|
try {
|
|
|
|
documentUri = new URI(savedInstanceState.getString(KEY_DOCUMENT_URI));
|
|
|
|
urlToLoad = documentUri.toString();
|
|
|
|
} catch (URISyntaxException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
isDocEditable = savedInstanceState.getBoolean(KEY_IS_EDITABLE);
|
|
|
|
} else {
|
|
|
|
//User can't reach here but if he/she does then
|
|
|
|
Toast.makeText(this, getString(R.string.failed_to_load_file), Toast.LENGTH_SHORT).show();
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
mWebView = findViewById(R.id.browser);
|
|
|
|
|
|
|
|
WebSettings webSettings = mWebView.getSettings();
|
|
|
|
webSettings.setJavaScriptEnabled(true);
|
|
|
|
mWebView.addJavascriptInterface(this, "LOOLMessageHandler");
|
|
|
|
|
|
|
|
// allow debugging (when building the debug version); see details in
|
|
|
|
// https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews
|
2020-03-14 00:07:50 +01:00
|
|
|
boolean isChromeDebugEnabled = sPrefs.getBoolean("ENABLE_CHROME_DEBUGGING", false);
|
|
|
|
if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 || isChromeDebugEnabled) {
|
|
|
|
WebView.setWebContentsDebuggingEnabled(true);
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
2019-05-09 22:57:16 +02:00
|
|
|
|
2020-03-04 11:33:17 +01:00
|
|
|
getMainHandler();
|
2019-07-11 11:29:13 +02:00
|
|
|
|
2019-06-17 02:30:00 +02:00
|
|
|
clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
|
|
|
nativeMsgThread = new Thread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
Looper.prepare();
|
|
|
|
nativeLooper = Looper.myLooper();
|
|
|
|
nativeHandler = new Handler(nativeLooper);
|
|
|
|
Looper.loop();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
nativeMsgThread.start();
|
|
|
|
|
2019-05-09 22:57:16 +02:00
|
|
|
mWebView.setWebChromeClient(new WebChromeClient() {
|
|
|
|
@Override
|
|
|
|
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
|
|
|
|
if (valueCallback != null) {
|
|
|
|
valueCallback.onReceiveValue(null);
|
|
|
|
valueCallback = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
valueCallback = filePathCallback;
|
|
|
|
Intent intent = fileChooserParams.createIntent();
|
|
|
|
|
|
|
|
try {
|
|
|
|
intent.setType("image/*");
|
2020-02-11 15:39:03 +01:00
|
|
|
startActivityForResult(intent, REQUEST_SELECT_IMAGE_FILE);
|
2019-05-09 22:57:16 +02:00
|
|
|
} catch (ActivityNotFoundException e) {
|
|
|
|
valueCallback = null;
|
|
|
|
Toast.makeText(LOActivity.this, getString(R.string.cannot_open_file_chooser), Toast.LENGTH_LONG).show();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
2019-07-11 11:29:13 +02:00
|
|
|
|
2019-07-23 12:38:41 +02:00
|
|
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
2019-07-11 11:29:13 +02:00
|
|
|
Log.i(TAG, "asking for read storage permission");
|
|
|
|
ActivityCompat.requestPermissions(this,
|
2019-07-23 12:38:41 +02:00
|
|
|
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
|
|
|
PERMISSION_WRITE_EXTERNAL_STORAGE);
|
2019-07-11 11:29:13 +02:00
|
|
|
} else {
|
|
|
|
loadDocument();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-02 09:32:15 +01:00
|
|
|
@Override
|
|
|
|
protected void onNewIntent(Intent intent) {
|
|
|
|
|
|
|
|
Log.i(TAG, "onNewIntent");
|
|
|
|
|
|
|
|
if (documentLoaded) {
|
|
|
|
postMobileMessageNative("save dontTerminateEdit=true dontSaveIfUnmodified=true");
|
|
|
|
}
|
|
|
|
|
|
|
|
final Intent finalIntent = intent;
|
2020-03-18 10:46:50 +01:00
|
|
|
mProgressDialog.indeterminate(R.string.exiting);
|
2020-03-04 11:33:17 +01:00
|
|
|
getMainHandler().post(new Runnable() {
|
2020-03-02 09:32:15 +01:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
documentLoaded = false;
|
|
|
|
postMobileMessageNative("BYE");
|
|
|
|
copyTempBackToIntent();
|
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
mProgressDialog.dismiss();
|
|
|
|
setIntent(finalIntent);
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
super.onNewIntent(intent);
|
|
|
|
}
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
@Override
|
|
|
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
|
|
|
super.onSaveInstanceState(outState);
|
|
|
|
outState.putString(KEY_INTENT_URI, getIntent().getData().toString());
|
|
|
|
outState.putInt(KEY_PROVIDER_ID, providerId);
|
|
|
|
if (documentUri != null) {
|
|
|
|
outState.putString(KEY_DOCUMENT_URI, documentUri.toString());
|
|
|
|
}
|
|
|
|
//If this activity was opened via contentUri
|
|
|
|
outState.putBoolean(KEY_IS_EDITABLE, isDocEditable);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
|
|
|
switch (requestCode) {
|
2019-07-23 12:38:41 +02:00
|
|
|
case PERMISSION_WRITE_EXTERNAL_STORAGE:
|
2019-07-11 11:29:13 +02:00
|
|
|
if (permissions.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
|
|
loadDocument();
|
|
|
|
} else {
|
|
|
|
Toast.makeText(this, getString(R.string.storage_permission_required), Toast.LENGTH_SHORT).show();
|
2020-03-02 09:32:15 +01:00
|
|
|
finishAndRemoveTask();
|
2019-07-11 11:29:13 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-13 21:21:23 +01:00
|
|
|
/** When we get the file via a content: URI, we need to put it to a temp file. */
|
2019-07-11 11:29:13 +02:00
|
|
|
private boolean copyFileToTemp() {
|
|
|
|
ContentResolver contentResolver = getContentResolver();
|
2020-02-20 12:31:11 +01:00
|
|
|
InputStream inputStream = null;
|
|
|
|
OutputStream outputStream = null;
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
// 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 {
|
|
|
|
try {
|
2020-02-20 12:31:11 +01:00
|
|
|
Uri uri = getIntent().getData();
|
|
|
|
inputStream = contentResolver.openInputStream(uri);
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
mTempFile = File.createTempFile("LibreOffice", suffix, this.getCacheDir());
|
2020-02-20 12:31:11 +01:00
|
|
|
outputStream = new FileOutputStream(mTempFile);
|
2019-07-11 11:29:13 +02:00
|
|
|
|
2020-02-20 12:31:11 +01:00
|
|
|
byte[] buffer = new byte[1024];
|
|
|
|
int length;
|
2020-03-16 17:35:37 +01:00
|
|
|
long bytes = 0;
|
|
|
|
while ((length = inputStream.read(buffer)) != -1) {
|
2020-02-20 12:31:11 +01:00
|
|
|
outputStream.write(buffer, 0, length);
|
2020-03-16 17:35:37 +01:00
|
|
|
bytes += length;
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
2020-03-16 17:35:37 +01:00
|
|
|
|
|
|
|
Log.i(TAG, "Success copying " + bytes + " bytes from " + uri + " to " + mTempFile);
|
2019-07-11 11:29:13 +02:00
|
|
|
} finally {
|
2020-02-20 12:31:11 +01:00
|
|
|
if (inputStream != null)
|
|
|
|
inputStream.close();
|
|
|
|
if (outputStream != null)
|
|
|
|
outputStream.close();
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
|
|
|
} catch (FileNotFoundException e) {
|
2020-03-16 17:35:37 +01:00
|
|
|
Log.e(TAG, "file not found: " + e.getMessage());
|
2019-07-11 11:29:13 +02:00
|
|
|
return false;
|
|
|
|
} catch (IOException e) {
|
2020-03-16 17:35:37 +01:00
|
|
|
Log.e(TAG, "exception: " + e.getMessage());
|
2019-07-11 11:29:13 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-03-16 17:35:37 +01:00
|
|
|
|
|
|
|
return true;
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
|
|
|
|
2020-02-13 21:21:23 +01:00
|
|
|
/** Check that we have created a temp file, and if yes, copy it back to the content: URI. */
|
|
|
|
private void copyTempBackToIntent() {
|
|
|
|
if (!isDocEditable || mTempFile == null || getIntent().getData() == null || !getIntent().getData().getScheme().equals(ContentResolver.SCHEME_CONTENT))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ContentResolver contentResolver = getContentResolver();
|
2020-02-20 12:31:11 +01:00
|
|
|
InputStream inputStream = null;
|
|
|
|
OutputStream outputStream = null;
|
2020-02-13 21:21:23 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
2020-03-16 17:35:37 +01:00
|
|
|
inputStream = new FileInputStream(mTempFile);
|
|
|
|
|
2020-02-20 12:31:11 +01:00
|
|
|
Uri uri = getIntent().getData();
|
2020-07-03 22:38:23 +02:00
|
|
|
try {
|
|
|
|
outputStream = contentResolver.openOutputStream(uri, "wt");
|
|
|
|
}
|
|
|
|
catch (FileNotFoundException e) {
|
|
|
|
Log.i(TAG, "failed with the 'wt' mode, trying without: " + e.getMessage());
|
|
|
|
outputStream = contentResolver.openOutputStream(uri);
|
|
|
|
}
|
2020-02-20 12:31:11 +01:00
|
|
|
|
|
|
|
byte[] buffer = new byte[1024];
|
|
|
|
int length;
|
2020-03-16 17:35:37 +01:00
|
|
|
long bytes = 0;
|
|
|
|
while ((length = inputStream.read(buffer)) != -1) {
|
2020-02-20 12:31:11 +01:00
|
|
|
outputStream.write(buffer, 0, length);
|
2020-03-16 17:35:37 +01:00
|
|
|
bytes += length;
|
2020-02-13 21:21:23 +01:00
|
|
|
}
|
2020-03-16 17:35:37 +01:00
|
|
|
|
|
|
|
Log.i(TAG, "Success copying " + bytes + " bytes from " + mTempFile + " to " + uri);
|
2020-02-13 21:21:23 +01:00
|
|
|
} finally {
|
2020-02-20 12:31:11 +01:00
|
|
|
if (inputStream != null)
|
|
|
|
inputStream.close();
|
|
|
|
if (outputStream != null)
|
|
|
|
outputStream.close();
|
2020-02-13 21:21:23 +01:00
|
|
|
}
|
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
Log.e(TAG, "file not found: " + e.getMessage());
|
|
|
|
} catch (Exception e) {
|
|
|
|
Log.e(TAG, "exception: " + e.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
@Override
|
|
|
|
protected void onResume() {
|
|
|
|
super.onResume();
|
|
|
|
Log.i(TAG, "onResume..");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPause() {
|
android: Fix the LOActivity lifecycle in several ways.
First of all, we shouldn't destroy the document in onStop(), instead do
that in onDestroy(). onStop() should only (auto-)save the document, but
otherwise keep it open, so that when we return from the file picker
Activity, the caret is at the same position as it was before.
In addition to that, we should close any open document before we try to
open the new one (in createLOOLWSD).
And finally, when closing the document, we should wait until after the
LOOLWSD is completely torn down, so that we avoid the risk that the user
tries to load another document too quickly, and the messages from the JS
go into the old, still running LOOLWSD.
Change-Id: Ib775021bccc5d5bc69aae392a88dc62a7c05a8fa
2019-07-19 17:38:31 +02:00
|
|
|
// A Save similar to an autosave
|
2019-07-23 14:17:34 +02:00
|
|
|
if (documentLoaded)
|
|
|
|
postMobileMessageNative("save dontTerminateEdit=true dontSaveIfUnmodified=true");
|
2020-02-28 18:30:52 +01:00
|
|
|
|
|
|
|
super.onPause();
|
|
|
|
Log.d(TAG, "onPause() - hinting to save, we might need to return to the doc");
|
android: Fix the LOActivity lifecycle in several ways.
First of all, we shouldn't destroy the document in onStop(), instead do
that in onDestroy(). onStop() should only (auto-)save the document, but
otherwise keep it open, so that when we return from the file picker
Activity, the caret is at the same position as it was before.
In addition to that, we should close any open document before we try to
open the new one (in createLOOLWSD).
And finally, when closing the document, we should wait until after the
LOOLWSD is completely torn down, so that we avoid the risk that the user
tries to load another document too quickly, and the messages from the JS
go into the old, still running LOOLWSD.
Change-Id: Ib775021bccc5d5bc69aae392a88dc62a7c05a8fa
2019-07-19 17:38:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onDestroy() {
|
2019-06-17 02:30:00 +02:00
|
|
|
nativeLooper.quit();
|
2020-02-12 21:49:17 +01:00
|
|
|
|
|
|
|
// Remove the webview from the hierarchy & destroy
|
|
|
|
final ViewGroup viewGroup = (ViewGroup) mWebView.getParent();
|
|
|
|
if (viewGroup != null)
|
|
|
|
viewGroup.removeView(mWebView);
|
2020-02-06 09:58:46 +01:00
|
|
|
mWebView.destroy();
|
2020-07-13 18:21:28 +02:00
|
|
|
mWebView = null;
|
2020-02-06 14:27:01 +01:00
|
|
|
|
|
|
|
// Most probably the native part has already got a 'BYE' from
|
|
|
|
// finishWithProgress(), but it is actually better to send it twice
|
|
|
|
// than never, so let's call it from here too anyway
|
2020-02-20 12:41:14 +01:00
|
|
|
documentLoaded = false;
|
2019-07-11 11:29:13 +02:00
|
|
|
postMobileMessageNative("BYE");
|
2020-02-28 18:30:52 +01:00
|
|
|
|
|
|
|
mProgressDialog.dismiss();
|
|
|
|
|
|
|
|
super.onDestroy();
|
|
|
|
Log.i(TAG, "onDestroy() - we know we are leaving the document");
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
|
|
|
|
2019-05-09 22:57:16 +02:00
|
|
|
@Override
|
|
|
|
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
2020-02-11 17:53:35 +01:00
|
|
|
if (resultCode != RESULT_OK) {
|
2020-08-25 21:44:12 +02:00
|
|
|
if (requestCode == REQUEST_SELECT_IMAGE_FILE) {
|
|
|
|
valueCallback.onReceiveValue(null);
|
|
|
|
valueCallback = null;
|
|
|
|
}
|
2020-02-11 17:53:35 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-11 15:39:03 +01:00
|
|
|
switch (requestCode) {
|
|
|
|
case REQUEST_SELECT_IMAGE_FILE:
|
|
|
|
if (valueCallback == null)
|
|
|
|
return;
|
|
|
|
valueCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
|
|
|
|
valueCallback = null;
|
2019-05-09 22:57:16 +02:00
|
|
|
return;
|
2020-02-11 15:39:03 +01:00
|
|
|
case REQUEST_SAVEAS_PDF:
|
|
|
|
case REQUEST_SAVEAS_RTF:
|
|
|
|
case REQUEST_SAVEAS_ODT:
|
|
|
|
case REQUEST_SAVEAS_ODP:
|
|
|
|
case REQUEST_SAVEAS_ODS:
|
|
|
|
case REQUEST_SAVEAS_DOCX:
|
|
|
|
case REQUEST_SAVEAS_PPTX:
|
|
|
|
case REQUEST_SAVEAS_XLSX:
|
|
|
|
case REQUEST_SAVEAS_DOC:
|
|
|
|
case REQUEST_SAVEAS_PPT:
|
|
|
|
case REQUEST_SAVEAS_XLS:
|
2020-03-17 12:41:41 +01:00
|
|
|
case REQUEST_SAVEAS_EPUB:
|
2020-02-11 17:53:35 +01:00
|
|
|
if (intent == null) {
|
|
|
|
return;
|
|
|
|
}
|
2020-02-11 15:39:03 +01:00
|
|
|
String format = getFormatForRequestCode(requestCode);
|
|
|
|
if (format != null) {
|
2020-03-16 18:35:43 +01:00
|
|
|
InputStream inputStream = null;
|
|
|
|
OutputStream outputStream = null;
|
|
|
|
try {
|
|
|
|
final File tempFile = File.createTempFile("LibreOffice", "." + format, this.getCacheDir());
|
|
|
|
LOActivity.this.saveAs(tempFile.toURI().toString(), format);
|
|
|
|
|
|
|
|
inputStream = new FileInputStream(tempFile);
|
2020-07-03 22:38:23 +02:00
|
|
|
try {
|
|
|
|
outputStream = getContentResolver().openOutputStream(intent.getData(), "wt");
|
|
|
|
}
|
|
|
|
catch (FileNotFoundException e) {
|
|
|
|
Log.i(TAG, "failed with the 'wt' mode, trying without: " + e.getMessage());
|
|
|
|
outputStream = getContentResolver().openOutputStream(intent.getData());
|
|
|
|
}
|
2020-03-16 18:35:43 +01:00
|
|
|
|
2020-02-11 15:39:03 +01:00
|
|
|
byte[] buffer = new byte[4096];
|
|
|
|
int len;
|
2020-03-16 18:35:43 +01:00
|
|
|
while ((len = inputStream.read(buffer)) != -1) {
|
2020-02-11 15:39:03 +01:00
|
|
|
outputStream.write(buffer, 0, len);
|
|
|
|
}
|
|
|
|
outputStream.flush();
|
|
|
|
} catch (Exception e) {
|
|
|
|
Toast.makeText(this, "Something went wrong while Saving as: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
|
|
|
e.printStackTrace();
|
2020-03-16 18:35:43 +01:00
|
|
|
} finally {
|
|
|
|
try {
|
|
|
|
if (inputStream != null)
|
|
|
|
inputStream.close();
|
|
|
|
if (outputStream != null)
|
|
|
|
outputStream.close();
|
|
|
|
} catch (Exception e) {
|
|
|
|
}
|
2020-02-11 15:39:03 +01:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Toast.makeText(this, "Unknown request", Toast.LENGTH_LONG).show();
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getFormatForRequestCode(int requestCode) {
|
|
|
|
switch(requestCode) {
|
|
|
|
case REQUEST_SAVEAS_PDF: return "pdf";
|
|
|
|
case REQUEST_SAVEAS_RTF: return "rtf";
|
|
|
|
case REQUEST_SAVEAS_ODT: return "odt";
|
|
|
|
case REQUEST_SAVEAS_ODP: return "odp";
|
|
|
|
case REQUEST_SAVEAS_ODS: return "ods";
|
|
|
|
case REQUEST_SAVEAS_DOCX: return "docx";
|
|
|
|
case REQUEST_SAVEAS_PPTX: return "pptx";
|
|
|
|
case REQUEST_SAVEAS_XLSX: return "xlsx";
|
|
|
|
case REQUEST_SAVEAS_DOC: return "doc";
|
|
|
|
case REQUEST_SAVEAS_PPT: return "ppt";
|
|
|
|
case REQUEST_SAVEAS_XLS: return "xls";
|
2020-03-17 12:41:41 +01:00
|
|
|
case REQUEST_SAVEAS_EPUB: return "epub";
|
2019-05-09 22:57:16 +02:00
|
|
|
}
|
2020-02-11 15:39:03 +01:00
|
|
|
return null;
|
2019-05-09 22:57:16 +02:00
|
|
|
}
|
|
|
|
|
2020-02-21 13:39:38 +01:00
|
|
|
/** Show the Saving progress and finish the app. */
|
|
|
|
private void finishWithProgress() {
|
2020-03-18 10:46:50 +01:00
|
|
|
mProgressDialog.indeterminate(R.string.exiting);
|
2020-02-06 14:27:01 +01:00
|
|
|
|
|
|
|
// The 'BYE' takes a considerable amount of time, we need to post it
|
|
|
|
// so that it starts after the saving progress is actually shown
|
2020-03-04 11:33:17 +01:00
|
|
|
getMainHandler().post(new Runnable() {
|
2020-02-06 14:27:01 +01:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2020-02-20 12:41:14 +01:00
|
|
|
documentLoaded = false;
|
2020-02-06 14:27:01 +01:00
|
|
|
postMobileMessageNative("BYE");
|
2020-02-13 21:21:23 +01:00
|
|
|
copyTempBackToIntent();
|
2020-02-06 14:27:01 +01:00
|
|
|
|
2020-03-16 17:40:09 +01:00
|
|
|
runOnUiThread(new Runnable() {
|
2020-02-06 14:27:01 +01:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2020-02-28 18:30:52 +01:00
|
|
|
mProgressDialog.dismiss();
|
2020-02-06 14:27:01 +01:00
|
|
|
}
|
2020-03-16 17:40:09 +01:00
|
|
|
});
|
2020-02-06 14:27:01 +01:00
|
|
|
|
2020-03-02 09:32:15 +01:00
|
|
|
finishAndRemoveTask();
|
2020-02-06 14:27:01 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-14 16:49:36 +01:00
|
|
|
@Override
|
|
|
|
public void onBackPressed() {
|
|
|
|
if (mMobileWizardVisible)
|
|
|
|
{
|
|
|
|
// just return one level up in the mobile-wizard (or close it)
|
|
|
|
callFakeWebsocketOnMessage("'mobile: mobilewizardback'");
|
|
|
|
return;
|
2020-02-12 18:53:34 +01:00
|
|
|
} else if (mIsEditModeActive) {
|
|
|
|
callFakeWebsocketOnMessage("'mobile: readonlymode'");
|
|
|
|
return;
|
2020-01-14 16:49:36 +01:00
|
|
|
}
|
|
|
|
|
2020-02-06 14:27:01 +01:00
|
|
|
finishWithProgress();
|
2020-01-14 16:49:36 +01:00
|
|
|
}
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
private void loadDocument() {
|
2020-02-28 18:30:52 +01:00
|
|
|
mProgressDialog.determinate(R.string.loading);
|
|
|
|
|
2019-07-23 14:17:34 +02:00
|
|
|
// setup the LOOLWSD
|
|
|
|
ApplicationInfo applicationInfo = getApplicationInfo();
|
|
|
|
String dataDir = applicationInfo.dataDir;
|
|
|
|
Log.i(TAG, String.format("Initializing LibreOfficeKit, dataDir=%s\n", dataDir));
|
|
|
|
|
|
|
|
String cacheDir = getApplication().getCacheDir().getAbsolutePath();
|
|
|
|
String apkFile = getApplication().getPackageResourcePath();
|
|
|
|
AssetManager assetManager = getResources().getAssets();
|
|
|
|
|
|
|
|
createLOOLWSD(dataDir, cacheDir, apkFile, assetManager, urlToLoad);
|
|
|
|
|
|
|
|
// trigger the load of the document
|
2019-07-11 11:29:13 +02:00
|
|
|
String finalUrlToLoad = "file:///android_asset/dist/loleaflet.html?file_path=" +
|
|
|
|
urlToLoad + "&closebutton=1";
|
2019-12-11 11:46:52 +01:00
|
|
|
|
|
|
|
// set the language
|
2020-02-24 08:27:15 +01:00
|
|
|
String language = getResources().getConfiguration().locale.toLanguageTag();
|
|
|
|
|
|
|
|
Log.i(TAG, "Loading with language: " + language);
|
|
|
|
|
|
|
|
finalUrlToLoad += "&lang=" + language;
|
2019-12-11 11:46:52 +01:00
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
if (isDocEditable) {
|
|
|
|
finalUrlToLoad += "&permission=edit";
|
|
|
|
} else {
|
|
|
|
finalUrlToLoad += "&permission=readonly";
|
|
|
|
}
|
2019-12-11 11:46:52 +01:00
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
if (isDocDebuggable) {
|
|
|
|
finalUrlToLoad += "&debug=true";
|
|
|
|
}
|
2019-12-11 11:46:52 +01:00
|
|
|
|
|
|
|
// load the page
|
2019-07-11 11:29:13 +02:00
|
|
|
mWebView.loadUrl(finalUrlToLoad);
|
2019-07-23 14:17:34 +02:00
|
|
|
|
|
|
|
documentLoaded = true;
|
2019-12-18 18:10:04 +01:00
|
|
|
|
|
|
|
loadDocumentMillis = android.os.SystemClock.uptimeMillis();
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static {
|
|
|
|
System.loadLibrary("androidapp");
|
|
|
|
}
|
|
|
|
|
2020-03-13 19:21:14 +01:00
|
|
|
public SharedPreferences getPrefs() {
|
|
|
|
return sPrefs;
|
|
|
|
}
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
/**
|
|
|
|
* Initialize the LOOLWSD to load 'loadFileURL'.
|
|
|
|
*/
|
|
|
|
public native void createLOOLWSD(String dataDir, String cacheDir, String apkFile, AssetManager assetManager, String loadFileURL);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Passing messages from JS (instead of the websocket communication).
|
|
|
|
*/
|
|
|
|
@JavascriptInterface
|
|
|
|
public void postMobileMessage(String message) {
|
|
|
|
Log.d(TAG, "postMobileMessage: " + message);
|
|
|
|
|
2020-01-15 21:08:26 +01:00
|
|
|
String[] messageAndParameterArray= message.split(" ", 2); // the command and the rest (that can potentially contain spaces too)
|
|
|
|
|
|
|
|
if (beforeMessageFromWebView(messageAndParameterArray)) {
|
2019-07-11 11:29:13 +02:00
|
|
|
postMobileMessageNative(message);
|
2020-01-15 21:08:26 +01:00
|
|
|
afterMessageFromWebView(messageAndParameterArray);
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call the post method form C++
|
|
|
|
*/
|
|
|
|
public native void postMobileMessageNative(String message);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Passing messages from JS (instead of the websocket communication).
|
|
|
|
*/
|
|
|
|
@JavascriptInterface
|
|
|
|
public void postMobileError(String message) {
|
|
|
|
// TODO handle this
|
|
|
|
Log.d(TAG, "postMobileError: " + message);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Passing messages from JS (instead of the websocket communication).
|
|
|
|
*/
|
|
|
|
@JavascriptInterface
|
|
|
|
public void postMobileDebug(String message) {
|
|
|
|
// TODO handle this
|
|
|
|
Log.d(TAG, "postMobileDebug: " + message);
|
|
|
|
}
|
|
|
|
|
2020-07-15 21:29:23 +02:00
|
|
|
/**
|
|
|
|
* Provide the info that this app is actually running under ChromeOS - so
|
|
|
|
* has to mostly look like on desktop.
|
|
|
|
*/
|
|
|
|
@JavascriptInterface
|
|
|
|
public boolean isChromeOS() {
|
|
|
|
return isChromeOS(this);
|
|
|
|
}
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
/**
|
|
|
|
* Passing message the other way around - from Java to the FakeWebSocket in JS.
|
|
|
|
*/
|
|
|
|
void callFakeWebsocketOnMessage(final String message) {
|
|
|
|
// call from the UI thread
|
2020-07-13 18:21:28 +02:00
|
|
|
if (mWebView != null)
|
|
|
|
mWebView.post(new Runnable() {
|
|
|
|
public void run() {
|
2020-07-14 17:07:01 +02:00
|
|
|
if (mWebView == null) {
|
2020-07-13 18:21:28 +02:00
|
|
|
Log.i(TAG, "Skipped forwarding to the WebView: " + message);
|
2020-07-14 17:07:01 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-05-07 16:16:03 +02:00
|
|
|
|
2020-07-13 18:21:28 +02:00
|
|
|
Log.i(TAG, "Forwarding to the WebView: " + message);
|
2020-05-07 16:16:03 +02:00
|
|
|
|
2020-07-13 18:21:28 +02:00
|
|
|
/* Debug only: in case the message is too long, truncated in the logcat, and you need to see it.
|
|
|
|
final int size = 80;
|
|
|
|
for (int start = 0; start < message.length(); start += size) {
|
|
|
|
Log.i(TAG, "split: " + message.substring(start, Math.min(message.length(), start + size)));
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
mWebView.loadUrl("javascript:window.TheFakeWebSocket.onmessage({'data':" + message + "});");
|
|
|
|
}
|
|
|
|
});
|
2020-02-28 18:30:52 +01:00
|
|
|
|
|
|
|
// update progress bar when loading
|
2020-03-16 11:20:51 +01:00
|
|
|
if (message.startsWith("'statusindicator") || message.startsWith("'error:")) {
|
2020-02-28 18:30:52 +01:00
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
public void run() {
|
|
|
|
// update progress bar if it exists
|
|
|
|
final String statusIndicatorSetValue = "'statusindicatorsetvalue: ";
|
|
|
|
if (message.startsWith(statusIndicatorSetValue)) {
|
|
|
|
int start = statusIndicatorSetValue.length();
|
|
|
|
int end = message.indexOf("'", start);
|
|
|
|
|
|
|
|
int progress = 0;
|
|
|
|
try {
|
|
|
|
progress = Integer.parseInt(message.substring(start, end));
|
|
|
|
} catch (Exception e) {
|
|
|
|
}
|
|
|
|
|
|
|
|
mProgressDialog.determinateProgress(progress);
|
|
|
|
}
|
2020-03-16 11:20:51 +01:00
|
|
|
else if (message.startsWith("'statusindicatorfinish:") || message.startsWith("'error:")) {
|
2020-02-28 18:30:52 +01:00
|
|
|
mProgressDialog.dismiss();
|
2020-03-17 23:05:39 +01:00
|
|
|
if (BuildConfig.GOOGLE_PLAY_ENABLED && rateAppController != null)
|
|
|
|
rateAppController.askUserForRating();
|
2020-02-28 18:30:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-17 02:30:00 +02:00
|
|
|
* return true to pass the message to the native part or false to block the message
|
2019-07-11 11:29:13 +02:00
|
|
|
*/
|
2020-01-15 21:08:26 +01:00
|
|
|
private boolean beforeMessageFromWebView(String[] messageAndParam) {
|
2019-08-10 00:21:50 +02:00
|
|
|
switch (messageAndParam[0]) {
|
2020-02-06 14:27:01 +01:00
|
|
|
case "BYE":
|
|
|
|
finishWithProgress();
|
|
|
|
return false;
|
2019-06-17 02:30:00 +02:00
|
|
|
case "PRINT":
|
2020-03-04 11:33:17 +01:00
|
|
|
getMainHandler().post(new Runnable() {
|
2019-06-17 02:30:00 +02:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
LOActivity.this.initiatePrint();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return false;
|
|
|
|
case "SLIDESHOW":
|
|
|
|
initiateSlideShow();
|
|
|
|
return false;
|
2019-08-10 00:21:50 +02:00
|
|
|
case "SAVE":
|
2020-02-13 21:21:23 +01:00
|
|
|
copyTempBackToIntent();
|
2019-08-10 00:21:50 +02:00
|
|
|
sendBroadcast(messageAndParam[0], messageAndParam[1]);
|
|
|
|
return false;
|
2020-02-11 15:39:03 +01:00
|
|
|
case "downloadas":
|
|
|
|
initiateSaveAs(messageAndParam[1]);
|
|
|
|
return false;
|
2019-08-10 00:21:50 +02:00
|
|
|
case "uno":
|
|
|
|
switch (messageAndParam[1]) {
|
|
|
|
case ".uno:Paste":
|
2019-12-18 18:10:04 +01:00
|
|
|
return performPaste();
|
|
|
|
default:
|
2019-08-10 00:21:50 +02:00
|
|
|
break;
|
|
|
|
}
|
2019-12-12 10:14:34 +01:00
|
|
|
break;
|
2019-07-28 01:43:26 +02:00
|
|
|
case "DIM_SCREEN": {
|
2020-03-04 11:33:17 +01:00
|
|
|
getMainHandler().post(new Runnable() {
|
2019-07-28 01:43:26 +02:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
case "LIGHT_SCREEN": {
|
2020-03-04 11:33:17 +01:00
|
|
|
getMainHandler().post(new Runnable() {
|
2019-07-28 01:43:26 +02:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return false;
|
|
|
|
}
|
2020-01-14 16:49:36 +01:00
|
|
|
case "MOBILEWIZARD": {
|
|
|
|
switch (messageAndParam[1]) {
|
|
|
|
case "show":
|
|
|
|
mMobileWizardVisible = true;
|
|
|
|
break;
|
|
|
|
case "hide":
|
|
|
|
mMobileWizardVisible = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
2020-02-14 15:22:46 +01:00
|
|
|
}
|
|
|
|
case "HYPERLINK": {
|
|
|
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
|
|
intent.setData(Uri.parse(messageAndParam[1]));
|
|
|
|
startActivity(intent);
|
|
|
|
return false;
|
2020-01-14 16:49:36 +01:00
|
|
|
}
|
2020-02-12 18:53:34 +01:00
|
|
|
case "EDITMODE": {
|
|
|
|
switch (messageAndParam[1]) {
|
|
|
|
case "on":
|
|
|
|
mIsEditModeActive = true;
|
|
|
|
break;
|
|
|
|
case "off":
|
|
|
|
mIsEditModeActive = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2020-09-10 16:04:36 +02:00
|
|
|
case "loadwithpassword": {
|
|
|
|
mProgressDialog.determinate(R.string.loading);
|
|
|
|
return true;
|
|
|
|
}
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-11 15:39:03 +01:00
|
|
|
private void initiateSaveAs(String optionsString) {
|
|
|
|
Map<String, String> optionsMap = new HashMap<>();
|
|
|
|
String[] options = optionsString.split(" ");
|
|
|
|
for (String option : options) {
|
|
|
|
String[] keyValue = option.split("=", 2);
|
|
|
|
if (keyValue.length == 2)
|
|
|
|
optionsMap.put(keyValue[0], keyValue[1]);
|
|
|
|
}
|
|
|
|
String format = optionsMap.get("format");
|
|
|
|
String mime = getMimeForFormat(format);
|
|
|
|
if (format != null && mime != null) {
|
|
|
|
String filename = optionsMap.get("name");
|
|
|
|
if (filename == null)
|
|
|
|
filename = "document." + format;
|
|
|
|
int requestID = getRequestIDForFormat(format);
|
|
|
|
|
|
|
|
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
|
|
|
intent.setType(mime);
|
|
|
|
intent.putExtra(Intent.EXTRA_TITLE, filename);
|
|
|
|
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, false);
|
|
|
|
File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
|
|
|
|
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Uri.fromFile(folder).toString());
|
|
|
|
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
|
|
|
|
startActivityForResult(intent, requestID);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private int getRequestIDForFormat(String format) {
|
|
|
|
switch (format) {
|
|
|
|
case "pdf": return REQUEST_SAVEAS_PDF;
|
|
|
|
case "rtf": return REQUEST_SAVEAS_RTF;
|
|
|
|
case "odt": return REQUEST_SAVEAS_ODT;
|
|
|
|
case "odp": return REQUEST_SAVEAS_ODP;
|
|
|
|
case "ods": return REQUEST_SAVEAS_ODS;
|
|
|
|
case "docx": return REQUEST_SAVEAS_DOCX;
|
|
|
|
case "pptx": return REQUEST_SAVEAS_PPTX;
|
|
|
|
case "xlsx": return REQUEST_SAVEAS_XLSX;
|
|
|
|
case "doc": return REQUEST_SAVEAS_DOC;
|
|
|
|
case "ppt": return REQUEST_SAVEAS_PPT;
|
|
|
|
case "xls": return REQUEST_SAVEAS_XLS;
|
2020-03-17 12:41:41 +01:00
|
|
|
case "epub": return REQUEST_SAVEAS_EPUB;
|
2020-02-11 15:39:03 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getMimeForFormat(String format) {
|
|
|
|
switch(format) {
|
|
|
|
case "pdf": return "application/pdf";
|
|
|
|
case "rtf": return "text/rtf";
|
|
|
|
case "odt": return "application/vnd.oasis.opendocument.text";
|
|
|
|
case "odp": return "application/vnd.oasis.opendocument.presentation";
|
|
|
|
case "ods": return "application/vnd.oasis.opendocument.spreadsheet";
|
|
|
|
case "docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
|
|
case "pptx": return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
|
|
|
case "xlsx": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
|
|
case "doc": return "application/msword";
|
|
|
|
case "ppt": return "application/vnd.ms-powerpoint";
|
|
|
|
case "xls": return "application/vnd.ms-excel";
|
2020-03-17 12:41:41 +01:00
|
|
|
case "epub": return "application/epub+zip";
|
2020-02-11 15:39:03 +01:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-01-15 21:08:26 +01:00
|
|
|
private void afterMessageFromWebView(String[] messageAndParameterArray) {
|
|
|
|
switch (messageAndParameterArray[0]) {
|
|
|
|
case "uno":
|
|
|
|
switch (messageAndParameterArray[1]) {
|
|
|
|
case ".uno:Copy":
|
|
|
|
case ".uno:Cut":
|
|
|
|
populateClipboard();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
private void initiatePrint() {
|
|
|
|
PrintManager printManager = (PrintManager) getSystemService(PRINT_SERVICE);
|
|
|
|
PrintDocumentAdapter printAdapter = new PrintAdapter(LOActivity.this);
|
|
|
|
printManager.print("Document", printAdapter, new PrintAttributes.Builder().build());
|
|
|
|
}
|
|
|
|
|
|
|
|
private void initiateSlideShow() {
|
2020-02-28 18:30:52 +01:00
|
|
|
mProgressDialog.indeterminate(R.string.loading);
|
2019-12-10 22:55:36 +01:00
|
|
|
|
2019-06-17 02:30:00 +02:00
|
|
|
nativeHandler.post(new Runnable() {
|
2019-07-11 11:29:13 +02:00
|
|
|
@Override
|
2019-06-17 02:30:00 +02:00
|
|
|
public void run() {
|
2019-07-11 11:29:13 +02:00
|
|
|
Log.v(TAG, "saving svg for slideshow by " + Thread.currentThread().getName());
|
2019-06-17 02:30:00 +02:00
|
|
|
final String slideShowFileUri = new File(LOActivity.this.getCacheDir(), "slideShow.svg").toURI().toString();
|
|
|
|
LOActivity.this.saveAs(slideShowFileUri, "svg");
|
|
|
|
LOActivity.this.runOnUiThread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2020-02-28 18:30:52 +01:00
|
|
|
mProgressDialog.dismiss();
|
2019-06-17 02:30:00 +02:00
|
|
|
Intent slideShowActIntent = new Intent(LOActivity.this, SlideShowActivity.class);
|
|
|
|
slideShowActIntent.putExtra(SlideShowActivity.SVG_URI_KEY, slideShowFileUri);
|
|
|
|
LOActivity.this.startActivity(slideShowActIntent);
|
|
|
|
}
|
|
|
|
});
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
2019-06-17 02:30:00 +02:00
|
|
|
});
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 16:12:52 +01:00
|
|
|
/** Send message back to the shell (for example for the cloud save). */
|
|
|
|
public void sendBroadcast(String event, String data) {
|
|
|
|
Intent intent = new Intent(LO_ACTIVITY_BROADCAST);
|
|
|
|
intent.putExtra(LO_ACTION_EVENT, event);
|
|
|
|
intent.putExtra(LO_ACTION_DATA, data);
|
|
|
|
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
|
|
|
|
}
|
2019-08-10 00:21:50 +02:00
|
|
|
|
2019-07-11 11:29:13 +02:00
|
|
|
public native void saveAs(String fileUri, String format);
|
|
|
|
|
2020-01-15 10:32:42 +01:00
|
|
|
public native boolean getClipboardContent(LokClipboardData aData);
|
|
|
|
|
|
|
|
public native void setClipboardContent(LokClipboardData aData);
|
|
|
|
|
|
|
|
public native void paste(String mimeType, byte[] data);
|
2019-06-17 02:30:00 +02:00
|
|
|
|
2020-01-15 10:32:42 +01:00
|
|
|
public native void postUnoCommand(String command, String arguments, boolean bNotifyWhenFinished);
|
2019-12-18 18:10:04 +01:00
|
|
|
|
|
|
|
/// Returns a magic that specifies this application - and this document.
|
|
|
|
private final String getClipboardMagic() {
|
2020-01-15 10:32:42 +01:00
|
|
|
return CLIPBOARD_LOOL_SIGNATURE + Long.toString(loadDocumentMillis);
|
2019-12-18 18:10:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Needs to be executed after the .uno:Copy / Paste has executed
|
|
|
|
public final void populateClipboard()
|
|
|
|
{
|
2020-01-15 21:08:26 +01:00
|
|
|
File clipboardFile = new File(getApplicationContext().getCacheDir(), CLIPBOARD_FILE_PATH);
|
|
|
|
if (clipboardFile.exists())
|
|
|
|
clipboardFile.delete();
|
|
|
|
|
|
|
|
LokClipboardData clipboardData = new LokClipboardData();
|
|
|
|
if (!LOActivity.this.getClipboardContent(clipboardData))
|
|
|
|
Log.e(TAG, "no clipboard to copy");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
clipboardData.writeToFile(clipboardFile);
|
|
|
|
|
|
|
|
String text = clipboardData.getText();
|
|
|
|
String html = clipboardData.getHtml();
|
|
|
|
|
|
|
|
if (html != null) {
|
|
|
|
int idx = html.indexOf("<meta name=\"generator\" content=\"");
|
|
|
|
|
|
|
|
if (idx < 0)
|
|
|
|
idx = html.indexOf("<meta http-equiv=\"content-type\" content=\"text/html;");
|
|
|
|
|
|
|
|
if (idx >= 0) { // inject our magic
|
|
|
|
StringBuffer newHtml = new StringBuffer(html);
|
|
|
|
newHtml.insert(idx, "<meta name=\"origin\" content=\"" + getClipboardMagic() + "\"/>\n");
|
|
|
|
html = newHtml.toString();
|
2019-12-18 18:10:04 +01:00
|
|
|
}
|
2020-01-15 21:08:26 +01:00
|
|
|
|
|
|
|
if (text == null || text.length() == 0)
|
|
|
|
Log.i(TAG, "set text to clipoard with: text '" + text + "' and html '" + html + "'");
|
|
|
|
|
|
|
|
clipData = ClipData.newHtmlText(ClipDescription.MIMETYPE_TEXT_HTML, text, html);
|
|
|
|
clipboardManager.setPrimaryClip(clipData);
|
|
|
|
}
|
|
|
|
}
|
2019-12-18 18:10:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Do the paste, and return true if we should short-circuit the paste locally
|
|
|
|
private final boolean performPaste()
|
|
|
|
{
|
|
|
|
clipData = clipboardManager.getPrimaryClip();
|
|
|
|
ClipDescription clipDesc = clipData != null ? clipData.getDescription() : null;
|
|
|
|
if (clipDesc != null) {
|
|
|
|
if (clipDesc.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML)) {
|
|
|
|
final String html = clipData.getItemAt(0).getHtmlText();
|
2020-01-15 10:32:42 +01:00
|
|
|
// Check if the clipboard content was made with the app
|
|
|
|
if (html.contains(CLIPBOARD_LOOL_SIGNATURE)) {
|
|
|
|
// Check if the clipboard content is from the same app instance
|
|
|
|
if (html.contains(getClipboardMagic())) {
|
|
|
|
Log.i(TAG, "clipboard comes from us - same instance: short circuit it " + html);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
Log.i(TAG, "clipboard comes from us - other instance: paste from clipboard file");
|
2020-01-15 21:08:26 +01:00
|
|
|
|
|
|
|
File clipboardFile = new File(getApplicationContext().getCacheDir(), CLIPBOARD_FILE_PATH);
|
|
|
|
LokClipboardData clipboardData = null;
|
2020-03-17 22:06:50 +01:00
|
|
|
if (clipboardFile.exists())
|
|
|
|
clipboardData = LokClipboardData.createFromFile(clipboardFile);
|
|
|
|
|
2020-01-15 21:08:26 +01:00
|
|
|
if (clipboardData != null) {
|
|
|
|
LOActivity.this.setClipboardContent(clipboardData);
|
|
|
|
LOActivity.this.postUnoCommand(".uno:Paste", null, false);
|
|
|
|
} else {
|
|
|
|
// Couldn't get data from the clipboard file, but we can still paste html
|
2020-01-15 10:32:42 +01:00
|
|
|
byte[] htmlByteArray = html.getBytes(Charset.forName("UTF-8"));
|
|
|
|
LOActivity.this.paste("text/html", htmlByteArray);
|
2019-12-18 18:10:04 +01:00
|
|
|
}
|
2020-01-15 21:08:26 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Log.i(TAG, "foreign html '" + html + "'");
|
|
|
|
byte[] htmlByteArray = html.getBytes(Charset.forName("UTF-8"));
|
|
|
|
LOActivity.this.paste("text/html", htmlByteArray);
|
2019-12-18 18:10:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (clipDesc.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
|
|
|
|
final ClipData.Item clipItem = clipData.getItemAt(0);
|
2020-01-15 21:08:26 +01:00
|
|
|
String text = clipItem.getText().toString();
|
|
|
|
byte[] textByteArray = text.getBytes(Charset.forName("UTF-16"));
|
|
|
|
LOActivity.this.paste("text/plain;charset=utf-16", textByteArray);
|
2019-12-18 18:10:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-07-11 11:29:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|