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:
parent
c6f36965cf
commit
07c3a7bdbb
7 changed files with 146 additions and 27 deletions
|
@ -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: */
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -97,7 +97,7 @@ public class LocalFile implements IFile {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void saveDocument(File file) {
|
||||
public void saveDocument() {
|
||||
// do nothing; file is local
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue