android: Implement uploading back to the cloud storage.

All these cases should be covered:

* user's explicit save via File -> Save
* autosave
* autosave on exit

Implemented via IntentFilter magic, we can call back from one activity
to the other to perform the actual saving in the shell.

Change-Id: I97d6e94028a9600a71f030af7146ee01163d09b8
Reviewed-on: https://gerrit.libreoffice.org/84872
Reviewed-by: Jan Holesovsky <kendy@collabora.com>
Tested-by: Jan Holesovsky <kendy@collabora.com>
This commit is contained in:
Jan Holesovsky 2019-12-10 16:12:52 +01:00
parent c6f36965cf
commit 07c3a7bdbb
7 changed files with 146 additions and 27 deletions

View file

@ -68,3 +68,5 @@ public interface IDocumentProvider {
*/
boolean checkProviderAvailability(Context context);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -112,5 +112,7 @@ public interface IFile {
* @param file
* A local file pointing to the new version of the document.
*/
void saveDocument(File file);
void saveDocument();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -34,7 +34,10 @@ public class ExternalFile implements IFile{
private ExtsdDocumentsProvider provider;
private DocumentFile docFile;
private File duplicateFile;
/** We create the document just once, cache it for further returning. */
private File mCachedFile;
private Context context;
public ExternalFile(ExtsdDocumentsProvider provider, DocumentFile docFile, Context context) {
@ -112,12 +115,14 @@ public class ExternalFile implements IFile{
@Override
public File getDocument() {
if(isDirectory()) {
if (mCachedFile != null)
return mCachedFile;
if (isDirectory())
return null;
} else {
duplicateFile = duplicateInCache();
return duplicateFile;
}
mCachedFile = duplicateInCache();
return mCachedFile;
}
private File duplicateInCache() {
@ -138,11 +143,16 @@ public class ExternalFile implements IFile{
}
@Override
public void saveDocument(File file) {
public void saveDocument() {
if (mCachedFile == null) {
Log.e(LOGTAG, "Trying to save document that was not created via getDocument()(");
return;
}
try{
OutputStream ostream = context.getContentResolver().
openOutputStream(docFile.getUri());
InputStream istream = new FileInputStream(file);
InputStream istream = new FileInputStream(mCachedFile);
IOUtils.copy(istream, ostream);
@ -162,3 +172,5 @@ public class ExternalFile implements IFile{
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -97,7 +97,7 @@ public class LocalFile implements IFile {
}
@Override
public void saveDocument(File file) {
public void saveDocument() {
// do nothing; file is local
}
}

View file

@ -1,6 +1,7 @@
package org.libreoffice.androidapp.storage.owncloud;
import android.content.Context;
import android.util.Log;
import java.io.File;
import java.io.FileFilter;
@ -25,10 +26,14 @@ import com.owncloud.android.lib.resources.files.model.RemoteFile;
* Implementation of IFile for ownCloud servers.
*/
public class OwnCloudFile implements IFile {
final static String LOGTAG = "OwnCloudFile";
private OwnCloudProvider provider;
private RemoteFile file;
/** We create the document just once, cache it for further returning. */
private File mCachedFile;
private String name;
private String parentPath;
@ -136,9 +141,12 @@ public class OwnCloudFile implements IFile {
@Override
public File getDocument() {
if (isDirectory()) {
if (mCachedFile != null)
return mCachedFile;
if (isDirectory())
return null;
}
File downFolder = provider.getCacheDir();
DownloadFileRemoteOperation operation = new DownloadFileRemoteOperation(
file.getRemotePath(), downFolder.getAbsolutePath());
@ -146,7 +154,9 @@ public class OwnCloudFile implements IFile {
if (!result.isSuccess()) {
throw provider.buildRuntimeExceptionForResultCode(result.getCode());
}
return new File(downFolder.getAbsolutePath() + file.getRemotePath());
mCachedFile = new File(downFolder.getAbsolutePath() + file.getRemotePath());
return mCachedFile;
}
@Override
@ -160,14 +170,19 @@ public class OwnCloudFile implements IFile {
}
@Override
public void saveDocument(File newFile) {
public void saveDocument() {
if (mCachedFile == null) {
Log.e(LOGTAG, "Trying to save document that was not created via getDocument()(");
return;
}
UploadFileRemoteOperation uploadOperation;
if (newFile.length() > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) {
if (mCachedFile.length() > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) {
uploadOperation = new ChunkedFileUploadRemoteOperation(
newFile.getPath(), file.getRemotePath(), file.getMimeType(), file.getEtag(), String.valueOf(file.getModifiedTimestamp()), false /* TODO actually check if on Wifi */);
mCachedFile.getPath(), file.getRemotePath(), file.getMimeType(), file.getEtag(), String.valueOf(mCachedFile.lastModified()), false /* TODO actually check if on Wifi */);
} else {
uploadOperation = new UploadFileRemoteOperation(newFile.getPath(),
file.getRemotePath(), file.getMimeType(), String.valueOf(file.getModifiedTimestamp()));
uploadOperation = new UploadFileRemoteOperation(mCachedFile.getPath(),
file.getRemotePath(), file.getMimeType(), String.valueOf(mCachedFile.lastModified()));
}
RemoteOperationResult result = uploadOperation.execute(provider
@ -177,3 +192,5 @@ public class OwnCloudFile implements IFile {
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View file

@ -95,6 +95,7 @@ import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -120,6 +121,9 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
private IFile currentDirectory;
private int currentlySelectedFile;
/** The document that is being edited - to know what to save back to cloud. */
private IFile mCurrentDocument;
private static final String CURRENT_DIRECTORY_KEY = "CURRENT_DIRECTORY";
private static final String DOC_PROVIDER_KEY = "CURRENT_DOCUMENT_PROVIDER";
private static final String FILTER_MODE_KEY = "FILTER_MODE";
@ -164,6 +168,9 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
private LinearLayout impressLayout;
private LinearLayout calcLayout;
/** Request code to evaluate that we are returning from the LOActivity. */
private static final int LO_ACTIVITY_REQUEST_CODE = 42;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -182,9 +189,12 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(mUSBReceiver, filter);
// Register the LOActivity events broadcast receiver
LocalBroadcastManager.getInstance(this).registerReceiver(mLOActivityReceiver,
new IntentFilter(LOActivity.LO_ACTIVITY_BROADCAST));
// init UI and populate with contents from the provider
createUI();
fabOpenAnimation = AnimationUtils.loadAnimation(this, R.anim.fab_open);
fabCloseAnimation = AnimationUtils.loadAnimation(this, R.anim.fab_close);
@ -540,6 +550,7 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
public void open(final IFile document) {
addDocumentToRecents(document);
mCurrentDocument = document;
new AsyncTask<IFile, Void, File>() {
@Override
protected File doInBackground(IFile... document) {
@ -566,8 +577,7 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
if (file != null) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.fromFile(file));
String packageName = getApplicationContext().getPackageName();
ComponentName componentName = new ComponentName(packageName,
LOActivity.class.getName());
ComponentName componentName = new ComponentName(packageName, LOActivity.class.getName());
i.setComponent(componentName);
// these extras allow to rebuild the IFile object in LOActivity
@ -576,7 +586,7 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
i.putExtra("org.libreoffice.document_uri",
document.getUri());
startActivity(i);
startActivityForResult(i, LO_ACTIVITY_REQUEST_CODE);
}
}
}.execute(document);
@ -616,7 +626,7 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
i.putExtra("org.libreoffice.document_provider_id", documentProvider.getId());
i.putExtra("org.libreoffice.document_uri", newDocUri);
startActivity(i);
startActivityForResult(i, LO_ACTIVITY_REQUEST_CODE);
} else {
Toast.makeText(LibreOfficeUIActivity.this, getString(R.string.file_creation_failed), Toast.LENGTH_SHORT).show();
}
@ -1022,6 +1032,67 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
}
};
/** Receiver for receiving messages from LOActivity - like that Save was performed and similar. */
private final BroadcastReceiver mLOActivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String event = intent.getStringExtra(LOActivity.LO_ACTION_EVENT);
Log.d(LOGTAG, "Received a message from LOActivity: " + event);
// Handle various events from LOActivity
if (event.equals("SAVE")) {
LibreOfficeUIActivity.this.saveFileToCloud();
}
}
};
/** Uploading back when we return from the LOActivity. */
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == LO_ACTIVITY_REQUEST_CODE) {
Log.d(LOGTAG, "LOActivity has finished.");
saveFileToCloud();
}
}
/** Actually copy the file to the remote storage (if applicable). */
public void saveFileToCloud() {
if (mCurrentDocument == null)
{
Log.e(LOGTAG, "No idea what should be uploaded.");
return;
}
final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
// call document provider save operation
mCurrentDocument.saveDocument();
}
catch (final RuntimeException e) {
LibreOfficeUIActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(LibreOfficeUIActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
Log.e(LOGTAG, e.getMessage(), e.getCause());
}
return null;
}
@Override
protected void onPostExecute(Void param) {
// FIXME Should we present a toast that the file was saved, or
// is it annoying?
//Toast.makeText(LibreOfficeUIActivity.this, R.string.message_saved, Toast.LENGTH_SHORT).show();
}
};
task.execute();
}
@Override
protected void onPause() {
super.onPause();
@ -1061,6 +1132,7 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mUSBReceiver);
unregisterReceiver(mLOActivityReceiver);
Log.d(LOGTAG, "onDestroy");
}
@ -1082,7 +1154,6 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
//put the new value in the first place
recentsArrayList.add(0, newRecent);
/*
* 4 because the number of recommended items in App Shortcuts is 4, and also
* because it's a good number of recent items in general

View file

@ -60,6 +60,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
public class LOActivity extends AppCompatActivity {
final static String TAG = "LOActivity";
@ -98,6 +99,15 @@ public class LOActivity extends AppCompatActivity {
private ValueCallback<Uri[]> valueCallback;
public static final int REQUEST_SELECT_FILE = 555;
/** 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";
private static boolean copyFromAssets(AssetManager assetManager,
String fromAssetPath, String targetDir) {
try {
@ -647,8 +657,13 @@ public class LOActivity extends AppCompatActivity {
});
}
/** Could be overridden if it's necessary to forward some callbacks elsewhere. */
public void sendBroadcast(String event, String data) {}
/** 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);
}
public native void saveAs(String fileUri, String format);