Merge pull request #967 from owncloud/fix_upload_fails_sending_another_app

Fixed fail in upload sent from the private storage of other app
This commit is contained in:
David A. Velasco 2015-05-05 13:51:46 +02:00
commit 8882fb2dbd
3 changed files with 324 additions and 87 deletions

View file

@ -219,6 +219,7 @@
<string name="filedisplay_unexpected_bad_get_content">"Unexpected problem ; please select the file from a different app"</string>
<string name="filedisplay_no_file_selected">No file was selected</string>
<string name="activity_chooser_title">Send link to &#8230;</string>
<string name="wait_for_tmp_copy_from_private_storage">Copying file from private storage</string>
<string name="oauth_check_onoff">Login with oAuth2</string>
<string name="oauth_login_connection">Connecting to oAuth2 server…</string>

View file

@ -2,6 +2,7 @@
* ownCloud Android client application
*
* @author Bartek Przybylski
* @author masensio
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
*
@ -29,6 +30,7 @@ import java.util.List;
import java.util.Stack;
import java.util.Vector;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.AlertDialog;
@ -47,9 +49,13 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
@ -71,6 +77,8 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.operations.CreateFolderOperation;
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
import com.owncloud.android.ui.dialog.LoadingDialog;
import com.owncloud.android.utils.CopyTmpFileAsyncTask;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.ErrorMessageAdapter;
@ -79,7 +87,8 @@ import com.owncloud.android.utils.ErrorMessageAdapter;
* This can be used to upload things to an ownCloud instance.
*/
public class Uploader extends FileActivity
implements OnItemClickListener, android.view.View.OnClickListener {
implements OnItemClickListener, android.view.View.OnClickListener,
CopyTmpFileAsyncTask.OnCopyTmpFileTaskListener {
private static final String TAG = Uploader.class.getSimpleName();
@ -90,6 +99,10 @@ public class Uploader extends FileActivity
private String mUploadPath;
private OCFile mFile;
private boolean mAccountSelected;
private boolean mAccountSelectionShowing;
private ArrayList<String> mRemoteCacheData;
private int mNumCacheFile;
private final static int DIALOG_NO_ACCOUNT = 0;
private final static int DIALOG_WAITING = 1;
@ -101,6 +114,11 @@ public class Uploader extends FileActivity
private final static String KEY_PARENTS = "PARENTS";
private final static String KEY_FILE = "FILE";
private final static String KEY_ACCOUNT_SELECTED = "ACCOUNT_SELECTED";
private final static String KEY_ACCOUNT_SELECTION_SHOWING = "ACCOUNT_SELECTION_SHOWING";
private final static String KEY_NUM_CACHE_FILE = "NUM_CACHE_FILE";
private final static String KEY_REMOTE_CACHE_DATA = "REMOTE_CACHE_DATA";
private static final String DIALOG_WAIT_COPY_FILE = "DIALOG_WAIT_COPY_FILE";
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -109,13 +127,27 @@ public class Uploader extends FileActivity
if (savedInstanceState == null) {
mParents = new Stack<String>();
mAccountSelected = false;
mAccountSelectionShowing = false;
mNumCacheFile = 0;
// ArrayList for files with path in private storage
mRemoteCacheData = new ArrayList<String>();
} else {
mParents = (Stack<String>) savedInstanceState.getSerializable(KEY_PARENTS);
mFile = savedInstanceState.getParcelable(KEY_FILE);
mAccountSelected = savedInstanceState.getBoolean(KEY_ACCOUNT_SELECTED);
mAccountSelectionShowing = savedInstanceState.getBoolean(KEY_ACCOUNT_SELECTION_SHOWING);
mNumCacheFile = savedInstanceState.getInt(KEY_NUM_CACHE_FILE);
mRemoteCacheData = savedInstanceState.getStringArrayList(KEY_REMOTE_CACHE_DATA);
}
super.onCreate(savedInstanceState);
if (mAccountSelected) {
setAccount((Account) savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT));
}
ActionBar actionBar = getSupportActionBar();
actionBar.setIcon(DisplayUtils.getSeasonalIconId());
@ -129,9 +161,10 @@ public class Uploader extends FileActivity
if (accounts.length == 0) {
Log_OC.i(TAG, "No ownCloud account is available");
showDialog(DIALOG_NO_ACCOUNT);
} else if (accounts.length > 1 && !mAccountSelected) {
} else if (accounts.length > 1 && !mAccountSelected && !mAccountSelectionShowing) {
Log_OC.i(TAG, "More than one ownCloud is available");
showDialog(DIALOG_MULTIPLE_ACCOUNT);
mAccountSelectionShowing = true;
} else {
if (!savedAccount) {
setAccount(accounts[0]);
@ -160,6 +193,10 @@ public class Uploader extends FileActivity
//outState.putParcelable(KEY_ACCOUNT, mAccount);
outState.putParcelable(KEY_FILE, mFile);
outState.putBoolean(KEY_ACCOUNT_SELECTED, mAccountSelected);
outState.putBoolean(KEY_ACCOUNT_SELECTION_SHOWING, mAccountSelectionShowing);
outState.putInt(KEY_NUM_CACHE_FILE, mNumCacheFile);
outState.putStringArrayList(KEY_REMOTE_CACHE_DATA, mRemoteCacheData);
outState.putParcelable(FileActivity.EXTRA_ACCOUNT, getAccount());
Log_OC.d(TAG, "onSaveInstanceState() end");
}
@ -225,12 +262,14 @@ public class Uploader extends FileActivity
onAccountSet(mAccountWasRestored);
dialog.dismiss();
mAccountSelected = true;
mAccountSelectionShowing = false;
}
});
builder.setCancelable(true);
builder.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
mAccountSelectionShowing = false;
dialog.cancel();
finish();
}
@ -366,7 +405,7 @@ public class Uploader extends FileActivity
actionBar.setHomeButtonEnabled(notRoot);
String full_path = generatePath(mParents);
Log_OC.d(TAG, "Populating view with content of : " + full_path);
mFile = getStorageManager().getFileByPath(full_path);
@ -421,6 +460,7 @@ public class Uploader extends FileActivity
public void uploadFiles() {
try {
// ArrayList for files with path in external storage
ArrayList<String> local = new ArrayList<String>();
ArrayList<String> remote = new ArrayList<String>();
@ -428,101 +468,106 @@ public class Uploader extends FileActivity
for (Parcelable mStream : mStreamsToUpload) {
Uri uri = (Uri) mStream;
if (uri !=null) {
String data = null;
String filePath = "";
if (uri != null) {
if (uri.getScheme().equals("content")) {
String mimeType = getContentResolver().getType(uri);
if (mimeType.contains("image")) {
String[] CONTENT_PROJECTION = { Images.Media.DATA,
Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE,
Images.Media.SIZE};
Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null,
null, null);
c.moveToFirst();
int index = c.getColumnIndex(Images.Media.DATA);
String data = c.getString(index);
local.add(data);
remote.add(mUploadPath +
c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME)));
}
else if (mimeType.contains("video")) {
String[] CONTENT_PROJECTION = { Video.Media.DATA,
String mimeType = getContentResolver().getType(uri);
if (mimeType.contains("image")) {
String[] CONTENT_PROJECTION = { Images.Media.DATA,
Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE,
Images.Media.SIZE };
Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null,
null, null);
c.moveToFirst();
int index = c.getColumnIndex(Images.Media.DATA);
data = c.getString(index);
filePath = mUploadPath +
c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME));
} else if (mimeType.contains("video")) {
String[] CONTENT_PROJECTION = { Video.Media.DATA,
Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE,
Video.Media.SIZE, Video.Media.DATE_MODIFIED };
Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null,
Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null,
null, null);
c.moveToFirst();
int index = c.getColumnIndex(Video.Media.DATA);
String data = c.getString(index);
local.add(data);
remote.add(mUploadPath +
c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME)));
c.moveToFirst();
int index = c.getColumnIndex(Video.Media.DATA);
data = c.getString(index);
filePath = mUploadPath +
c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME));
}
else if (mimeType.contains("audio")) {
String[] CONTENT_PROJECTION = { Audio.Media.DATA,
} else if (mimeType.contains("audio")) {
String[] CONTENT_PROJECTION = { Audio.Media.DATA,
Audio.Media.DISPLAY_NAME, Audio.Media.MIME_TYPE,
Audio.Media.SIZE };
Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null,
Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null,
null, null);
c.moveToFirst();
int index = c.getColumnIndex(Audio.Media.DATA);
String data = c.getString(index);
local.add(data);
remote.add(mUploadPath +
c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME)));
}
else {
String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() +
"://", "");
// cut everything whats before mnt. It occurred to me that sometimes
// apps send their name into the URI
if (filePath.contains("mnt")) {
String splitedFilePath[] = filePath.split("/mnt");
filePath = splitedFilePath[1];
}
final File file = new File(filePath);
local.add(file.getAbsolutePath());
remote.add(mUploadPath + file.getName());
}
c.moveToFirst();
int index = c.getColumnIndex(Audio.Media.DATA);
data = c.getString(index);
filePath = mUploadPath +
c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME));
} else {
Cursor cursor = getContentResolver().query(uri,
new String[]{MediaStore.MediaColumns.DISPLAY_NAME},
null, null, null);
cursor.moveToFirst();
int nameIndex = cursor.getColumnIndex(cursor.getColumnNames()[0]);
if (nameIndex >= 0) {
filePath = mUploadPath + cursor.getString(nameIndex);
}
}
} else if (uri.getScheme().equals("file")) {
String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() +
filePath = Uri.decode(uri.toString()).replace(uri.getScheme() +
"://", "");
if (filePath.contains("mnt")) {
String splitedFilePath[] = filePath.split("/mnt");
filePath = splitedFilePath[1];
}
final File file = new File(filePath);
local.add(file.getAbsolutePath());
remote.add(mUploadPath + file.getName());
data = file.getAbsolutePath();
filePath = mUploadPath + file.getName();
}
else {
throw new SecurityException();
}
if (data == null) {
mRemoteCacheData.add(filePath);
CopyTmpFileAsyncTask copyTask = new CopyTmpFileAsyncTask(this);
Object[] params = { uri, filePath, mRemoteCacheData.size()-1,
getAccount().name, getContentResolver()};
mNumCacheFile++;
showWaitingCopyDialog();
copyTask.execute(params);
} else {
remote.add(filePath);
local.add(data);
}
}
else {
throw new SecurityException();
}
Intent intent = new Intent(getApplicationContext(), FileUploader.class);
intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()]));
intent.putExtra(FileUploader.KEY_REMOTE_FILE,
remote.toArray(new String[remote.size()]));
intent.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
startService(intent);
//Save the path to shared preferences
SharedPreferences.Editor appPrefs = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()).edit();
appPrefs.putString("last_upload_path", mUploadPath);
appPrefs.apply();
Intent intent = new Intent(getApplicationContext(), FileUploader.class);
intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()]));
intent.putExtra(FileUploader.KEY_REMOTE_FILE,
remote.toArray(new String[remote.size()]));
intent.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
startService(intent);
finish();
//Save the path to shared preferences
SharedPreferences.Editor appPrefs = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()).edit();
appPrefs.putString("last_upload_path", mUploadPath);
appPrefs.apply();
finish();
}
} catch (SecurityException e) {
@ -588,16 +633,15 @@ public class Uploader extends FileActivity
// "/" equals root-directory
if(last_path.equals("/")) {
mParents.add("");
}
else{
} else{
String[] dir_names = last_path.split("/");
for (String dir : dir_names)
mParents.add(dir);
}
//Make sure that path still exists, if it doesn't pop the stack and try the previous path
while(!getStorageManager().fileExists(generatePath(mParents)) && mParents.size() > 1){
mParents.pop();
}
while(!getStorageManager().fileExists(generatePath(mParents)) && mParents.size() > 1){
mParents.pop();
}
}
@ -605,17 +649,67 @@ public class Uploader extends FileActivity
public boolean onOptionsItemSelected(MenuItem item) {
boolean retval = true;
switch (item.getItemId()) {
case android.R.id.home: {
if((mParents.size() > 1)) {
onBackPressed();
}
break;
}
default:
retval = super.onOptionsItemSelected(item);
case android.R.id.home:
if((mParents.size() > 1)) {
onBackPressed();
}
break;
default:
retval = super.onOptionsItemSelected(item);
}
return retval;
}
/**
* Process the result of CopyTmpFileAsyncTask
* @param result
* @param index
*/
@Override
public void onTmpFileCopied(String result, int index) {
if (mNumCacheFile -- == 0) {
dismissWaitingCopyDialog();
}
if (result != null) {
Intent intent = new Intent(getApplicationContext(), FileUploader.class);
intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
intent.putExtra(FileUploader.KEY_LOCAL_FILE, result);
intent.putExtra(FileUploader.KEY_REMOTE_FILE, mRemoteCacheData.get(index));
intent.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
startService(intent);
} else {
String message = String.format(getString(R.string.uploader_error_forbidden_content),
getString(R.string.app_name));
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
Log_OC.d(TAG, message);
}
}
/**
* Show waiting for copy dialog
*/
public void showWaitingCopyDialog() {
// Construct dialog
LoadingDialog loading = new LoadingDialog(
getResources().getString(R.string.wait_for_tmp_copy_from_private_storage));
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
loading.show(ft, DIALOG_WAIT_COPY_FILE);
}
/**
* Dismiss waiting for copy dialog
*/
public void dismissWaitingCopyDialog(){
Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_COPY_FILE);
if (frag != null) {
LoadingDialog loading = (LoadingDialog) frag;
loading.dismiss();
}
}
}

View file

@ -0,0 +1,142 @@
/**
* ownCloud Android client application
*
* @author masensio
* Copyright (C) 2015 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.owncloud.android.utils;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.AsyncTask;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.ref.WeakReference;
/**
* AsyncTask to copy a file from a uri in a temporal file
*/
public class CopyTmpFileAsyncTask extends AsyncTask<Object, Void, String> {
private final String TAG = CopyTmpFileAsyncTask.class.getSimpleName();
private final WeakReference<OnCopyTmpFileTaskListener> mListener;
private int mIndex;
public int getIndex(){
return mIndex;
}
public CopyTmpFileAsyncTask(OnCopyTmpFileTaskListener listener) {
mListener = new WeakReference<OnCopyTmpFileTaskListener>(listener);
}
/**
* Params for execute:
* - Uri: uri of file
* - String: path for saving the file into the app
* - int: index of upload
* - String: accountName
* - ContentResolver: content resolver
*/
@Override
protected String doInBackground(Object[] params) {
String result = null;
if (params != null && params.length == 5) {
Uri uri = (Uri) params[0];
String filePath = (String) params[1];
mIndex = ((Integer) params[2]).intValue();
String accountName = (String) params[3];
ContentResolver contentResolver = (ContentResolver) params[4];
String fullTempPath = FileStorageUtils.getTemporalPath(accountName) + filePath;
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = contentResolver.openInputStream(uri);
File cacheFile = new File(fullTempPath);
File tempDir = cacheFile.getParentFile();
if (!tempDir.exists()) {
tempDir.mkdirs();
}
cacheFile.createNewFile();
outputStream = new FileOutputStream(fullTempPath);
byte[] buffer = new byte[4096];
int count = 0;
while ((count = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, count);
}
outputStream.close();
inputStream.close();
result = fullTempPath;
} catch (Exception e) {
Log_OC.e(TAG, "Exception ", e);
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e1) {
Log_OC.e(TAG, "Input Stream Exception ", e1);
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (Exception e1) {
Log_OC.e(TAG, "Output Stream Exception ", e1);
}
}
if (fullTempPath != null) {
File f = new File(fullTempPath);
f.delete();
}
result = null;
}
} else {
throw new IllegalArgumentException("Error in parameters number");
}
return result;
}
@Override
protected void onPostExecute(String result) {
OnCopyTmpFileTaskListener listener = mListener.get();
if (listener!= null)
{
listener.onTmpFileCopied(result, mIndex);
}
}
/*
* Interface to retrieve data from recognition task
*/
public interface OnCopyTmpFileTaskListener{
void onTmpFileCopied(String result, int index);
}
}