Direct camera upload

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
AndyScherzinger 2018-09-26 14:13:03 +02:00 committed by tobiasKaminsky
parent 6ff68d52e8
commit 8ae93db343
No known key found for this signature in database
GPG Key ID: 0E00D4D47D0C5AF7
13 changed files with 427 additions and 206 deletions

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M4,4H7L9,2H15L17,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20H4A2,2 0 0,1 2,18V6A2,2 0 0,1 4,4M12,7A5,5 0 0,0 7,12A5,5 0 0,0 12,17A5,5 0 0,0 17,12A5,5 0 0,0 12,7M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9Z" /></svg>

After

Width:  |  Height:  |  Size: 518 B

View File

@ -1,5 +1,3 @@
# workaround since lombok and desugering have an issue
# to be fixed with gradle Android plugin 3.3.0
android.enableJetifier=true
android.useAndroidX=true
android.debug.obsoleteApi=true

View File

@ -96,6 +96,7 @@ import com.owncloud.android.operations.UpdateShareViaLinkOperation;
import com.owncloud.android.operations.UploadFileOperation;
import com.owncloud.android.providers.UsersAndGroupsSearchProvider;
import com.owncloud.android.syncadapter.FileSyncAdapter;
import com.owncloud.android.ui.asynctasks.CheckAvailableSpaceTask;
import com.owncloud.android.ui.dialog.SendShareDialog;
import com.owncloud.android.ui.dialog.ShareLinkToDialog;
import com.owncloud.android.ui.dialog.SortingOrderDialogFragment;
@ -180,6 +181,7 @@ public class FileDisplayActivity extends HookActivity
public static final int REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM = REQUEST_CODE__LAST_SHARED + 2;
public static final int REQUEST_CODE__MOVE_FILES = REQUEST_CODE__LAST_SHARED + 3;
public static final int REQUEST_CODE__COPY_FILES = REQUEST_CODE__LAST_SHARED + 4;
public static final int REQUEST_CODE__UPLOAD_FROM_CAMERA = REQUEST_CODE__LAST_SHARED + 5;
protected static final long DELAY_TO_REQUEST_REFRESH_OPERATION_LATER = DELAY_TO_REQUEST_OPERATIONS_LATER + 350;
@ -967,6 +969,33 @@ public class FileDisplayActivity extends HookActivity
requestUploadOfFilesFromFileSystem(data, resultCode);
} else if (requestCode == REQUEST_CODE__UPLOAD_FROM_CAMERA &&
(resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
new CheckAvailableSpaceTask(new CheckAvailableSpaceTask.CheckAvailableSpaceListener() {
@Override
public void onCheckAvailableSpaceStart() {
Log_OC.d(this, "onCheckAvailableSpaceStart");
}
@Override
public void onCheckAvailableSpaceFinish(boolean hasEnoughSpaceAvailable, String[] filesToUpload) {
Log_OC.d(this, "onCheckAvailableSpaceFinish");
if (hasEnoughSpaceAvailable) {
File file = new File(filesToUpload[0]);
File renamedFile = new File(file.getParent() + "/" + FileOperationsHelper.getCapturedImageName());
if (!file.renameTo(renamedFile)) {
DisplayUtils.showSnackMessage(getActivity(), "Fail to upload taken image!");
return;
}
requestUploadOfFilesFromFileSystem(new String[]{renamedFile.getAbsolutePath()},
FileUploader.LOCAL_BEHAVIOUR_MOVE);
}
}
}, new String[]{FileOperationsHelper.createImageFile(getActivity()).getAbsolutePath()}).execute();
} else if (requestCode == REQUEST_CODE__MOVE_FILES && resultCode == RESULT_OK) {
exitSelectionMode();
final Intent fData = data;
@ -992,11 +1021,9 @@ public class FileDisplayActivity extends HookActivity
},
DELAY_TO_REQUEST_OPERATIONS_LATER
);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void exitSelectionMode() {
@ -1008,6 +1035,17 @@ public class FileDisplayActivity extends HookActivity
private void requestUploadOfFilesFromFileSystem(Intent data, int resultCode) {
String[] filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);
int behaviour;
if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE) {
behaviour = FileUploader.LOCAL_BEHAVIOUR_MOVE;
} else {
behaviour = FileUploader.LOCAL_BEHAVIOUR_COPY;
}
requestUploadOfFilesFromFileSystem(filePaths, behaviour);
}
private void requestUploadOfFilesFromFileSystem(String[] filePaths, int resultCode) {
if (filePaths != null) {
String[] remotePaths = new String[filePaths.length];
String remotePathBase = getCurrentDir().getRemotePath();
@ -1036,16 +1074,16 @@ public class FileDisplayActivity extends HookActivity
FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
requester.uploadNewFile(
this,
getAccount(),
filePaths,
remotePaths,
null, // MIME type will be detected from file name
behaviour,
false, // do not create parent folder if not existent
UploadFileOperation.CREATED_BY_USER,
false,
false
this,
getAccount(),
filePaths,
remotePaths,
null, // MIME type will be detected from file name
behaviour,
false, // do not create parent folder if not existent
UploadFileOperation.CREATED_BY_USER,
false,
false
);
} else {

View File

@ -27,7 +27,6 @@ import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
@ -45,7 +44,9 @@ import android.widget.TextView;
import com.google.android.material.button.MaterialButton;
import com.owncloud.android.R;
import com.owncloud.android.db.PreferenceManager;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.ui.asynctasks.CheckAvailableSpaceTask;
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
@ -53,7 +54,6 @@ import com.owncloud.android.ui.dialog.SortingOrderDialogFragment;
import com.owncloud.android.ui.fragment.ExtendedListFragment;
import com.owncloud.android.ui.fragment.LocalFileListFragment;
import com.owncloud.android.utils.FileSortOrder;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.ThemeUtils;
import java.io.File;
@ -77,7 +77,8 @@ import androidx.fragment.app.FragmentTransaction;
*/
public class UploadFilesActivity extends FileActivity implements
LocalFileListFragment.ContainerActivity, ActionBar.OnNavigationListener,
OnClickListener, ConfirmationDialogFragmentListener, SortingOrderDialogFragment.OnSortingOrderListener {
OnClickListener, ConfirmationDialogFragmentListener, SortingOrderDialogFragment.OnSortingOrderListener,
CheckAvailableSpaceTask.CheckAvailableSpaceListener {
private static final String SORT_ORDER_DIALOG_TAG = "SORT_ORDER_DIALOG";
private static final int SINGLE_DIR = 1;
@ -113,6 +114,8 @@ public class UploadFilesActivity extends FileActivity implements
private static final String TAG = "UploadFilesActivity";
private static final String WAIT_DIALOG_TAG = "WAIT";
private static final String QUERY_TO_MOVE_DIALOG_TAG = "QUERY_TO_MOVE";
public static final String REQUEST_CODE_KEY = "requestCode";
private int requestCode;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -122,6 +125,7 @@ public class UploadFilesActivity extends FileActivity implements
Bundle extras = getIntent().getExtras();
if (extras != null) {
mLocalFolderPickerMode = extras.getBoolean(KEY_LOCAL_FOLDER_PICKER_MODE, false);
requestCode = (int) extras.get(REQUEST_CODE_KEY);
}
if (savedInstanceState != null) {
@ -182,12 +186,12 @@ public class UploadFilesActivity extends FileActivity implements
List<String> behaviours = new ArrayList<>();
behaviours.add(getString(R.string.uploader_upload_files_behaviour_move_to_nextcloud_folder,
ThemeUtils.getDefaultDisplayNameForRootFolder(this)));
ThemeUtils.getDefaultDisplayNameForRootFolder(this)));
behaviours.add(getString(R.string.uploader_upload_files_behaviour_only_upload));
behaviours.add(getString(R.string.uploader_upload_files_behaviour_upload_and_delete_from_source));
ArrayAdapter<String> behaviourAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item,
behaviours);
behaviours);
behaviourAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mBehaviourSpinner.setAdapter(behaviourAdapter);
mBehaviourSpinner.setSelection(localBehaviour);
@ -197,8 +201,7 @@ public class UploadFilesActivity extends FileActivity implements
// Action bar setup
ActionBar actionBar = getSupportActionBar();
actionBar.setHomeButtonEnabled(true); // mandatory since Android ICS, according to the
// official documentation
actionBar.setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getName() != null);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
@ -232,6 +235,7 @@ public class UploadFilesActivity extends FileActivity implements
public static void startUploadActivityForResult(Activity activity, Account account, int requestCode) {
Intent action = new Intent(activity, UploadFilesActivity.class);
action.putExtra(EXTRA_ACCOUNT, account);
action.putExtra(REQUEST_CODE_KEY, requestCode);
activity.startActivityForResult(action, requestCode);
}
@ -417,6 +421,73 @@ public class UploadFilesActivity extends FileActivity implements
}
}
@Override
public void onCheckAvailableSpaceStart() {
if (requestCode == FileDisplayActivity.REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM) {
mCurrentDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false);
mCurrentDialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
}
}
/**
* Updates the activity UI after the check of space is done. If there is not space enough. shows a new dialog to
* query the user if wants to move the files instead of copy them.
*
* @param hasEnoughSpaceAvailable 'True' when there is space enough to copy all the selected files.
*/
@Override
public void onCheckAvailableSpaceFinish(boolean hasEnoughSpaceAvailable, String[] filesToUpload) {
if (mCurrentDialog != null) {
mCurrentDialog.dismiss();
mCurrentDialog = null;
}
if (hasEnoughSpaceAvailable) {
// return the list of files (success)
Intent data = new Intent();
if (requestCode == FileDisplayActivity.REQUEST_CODE__UPLOAD_FROM_CAMERA) {
data.putExtra(EXTRA_CHOSEN_FILES, new String[]{filesToUpload[0]});
setResult(RESULT_OK_AND_MOVE, data);
PreferenceManager.setUploaderBehaviour(getApplicationContext(), FileUploader.LOCAL_BEHAVIOUR_MOVE);
} else {
data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
// set result code
switch (mBehaviourSpinner.getSelectedItemPosition()) {
case 0: // move to nextcloud folder
setResult(RESULT_OK_AND_MOVE, data);
break;
case 1: // only upload
setResult(RESULT_OK_AND_DO_NOTHING, data);
break;
case 2: // upload and delete from source
setResult(RESULT_OK_AND_DELETE, data);
break;
}
// store behaviour
PreferenceManager.setUploaderBehaviour(getApplicationContext(),
mBehaviourSpinner.getSelectedItemPosition());
}
finish();
} else {
// show a dialog to query the user if wants to move the selected files
// to the ownCloud folder instead of copying
String[] args = {getString(R.string.app_name)};
ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(
R.string.upload_query_move_foreign_files, args, 0, R.string.common_yes, -1,
R.string.common_no
);
dialog.setOnConfirmationListener(UploadFilesActivity.this);
dialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG);
}
}
/**
* Custom array adapter to override text colors
*/
@ -531,101 +602,8 @@ public class UploadFilesActivity extends FileActivity implements
finish();
} else {
new CheckAvailableSpaceTask().execute(mBehaviourSpinner.getSelectedItemPosition() == 0);
}
}
}
/**
* Asynchronous task checking if there is space enough to copy all the files chosen
* to upload into the ownCloud local folder.
*
* Maybe an AsyncTask is not strictly necessary, but who really knows.
*/
private class CheckAvailableSpaceTask extends AsyncTask<Boolean, Void, Boolean> {
/**
* Updates the UI before trying the movement.
*/
@Override
protected void onPreExecute () {
/// progress dialog and disable 'Move' button
mCurrentDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false);
mCurrentDialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
}
/**
* Checks the available space.
*
* @param params boolean flag if storage calculation should be done.
* @return 'True' if there is space enough or doesn't have to be calculated
*/
@Override
protected Boolean doInBackground(Boolean... params) {
if(params[0]) {
String[] checkedFilePaths = mFileListFragment.getCheckedFilePaths();
long total = 0;
for (int i = 0; checkedFilePaths != null && i < checkedFilePaths.length; i++) {
String localPath = checkedFilePaths[i];
File localFile = new File(localPath);
total += localFile.length();
}
return FileStorageUtils.getUsableSpace() >= total;
}
return true;
}
/**
* Updates the activity UI after the check of space is done.
*
* If there is not space enough. shows a new dialog to query the user if wants to move the
* files instead of copy them.
*
* @param result 'True' when there is space enough to copy all the selected files.
*/
@Override
protected void onPostExecute(Boolean result) {
if(mCurrentDialog != null) {
mCurrentDialog.dismiss();
mCurrentDialog = null;
}
if (result) {
// return the list of selected files (success)
Intent data = new Intent();
data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
// set result code
switch (mBehaviourSpinner.getSelectedItemPosition()) {
case 0: // move to nextcloud folder
setResult(RESULT_OK_AND_MOVE, data);
break;
case 1: // only upload
setResult(RESULT_OK_AND_DO_NOTHING, data);
break;
case 2: // upload and delete from source
setResult(RESULT_OK_AND_DELETE, data);
break;
}
// store behaviour
PreferenceManager.setUploaderBehaviour(getApplicationContext(),
mBehaviourSpinner.getSelectedItemPosition());
finish();
} else {
// show a dialog to query the user if wants to move the selected files
// to the ownCloud folder instead of copying
String[] args = {getString(R.string.app_name)};
ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(
R.string.upload_query_move_foreign_files, args, 0, R.string.common_yes, -1,
R.string.common_no
);
dialog.setOnConfirmationListener(UploadFilesActivity.this);
dialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG);
new CheckAvailableSpaceTask(this, mFileListFragment.getCheckedFilePaths())
.execute(mBehaviourSpinner.getSelectedItemPosition() == 0);
}
}
}

View File

@ -0,0 +1,79 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2019 Tobias Kaminsky
* Copyright (C) 2019 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.asynctasks;
import android.os.AsyncTask;
import com.owncloud.android.utils.FileStorageUtils;
import java.io.File;
/**
* Asynchronous task checking if there is space enough to copy all the files chosen to upload into the ownCloud local
* folder. Maybe an AsyncTask is not strictly necessary, but who really knows.
*/
public class CheckAvailableSpaceTask extends AsyncTask<Boolean, Void, Boolean> {
private String[] paths;
private CheckAvailableSpaceListener callback;
public CheckAvailableSpaceTask(CheckAvailableSpaceListener callback, String[] paths) {
this.paths = paths;
this.callback = callback;
}
/**
* Updates the UI before trying the movement.
*/
@Override
protected void onPreExecute() {
callback.onCheckAvailableSpaceStart();
}
/**
* Checks the available space.
*
* @param params boolean flag if storage calculation should be done.
* @return 'True' if there is space enough or doesn't have to be calculated
*/
@Override
protected Boolean doInBackground(Boolean... params) {
long total = 0;
for (int i = 0; paths != null && i < paths.length; i++) {
String localPath = paths[i];
File localFile = new File(localPath);
total += localFile.length();
}
return FileStorageUtils.getUsableSpace() >= total;
}
@Override
protected void onPostExecute(Boolean result) {
callback.onCheckAvailableSpaceFinish(result, paths);
}
public interface CheckAvailableSpaceListener {
void onCheckAvailableSpaceStart();
void onCheckAvailableSpaceFinish(boolean hasEnoughSpaceAvailable, String[] filesToUpload);
}
}

View File

@ -1,44 +1,42 @@
/**
* ownCloud Android client application
/*
* ownCloud Android client application
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
* Copyright (C) 2012 Bartek Przybylski 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 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/>.
* 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.ui.dialog;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import com.owncloud.android.R;
import com.owncloud.android.utils.ThemeUtils;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
public class ConfirmationDialogFragment extends DialogFragment {
public final static String ARG_MESSAGE_RESOURCE_ID = "resource_id";
public final static String ARG_MESSAGE_ARGUMENTS = "string_array";
final static String ARG_MESSAGE_RESOURCE_ID = "resource_id";
final static String ARG_MESSAGE_ARGUMENTS = "string_array";
private static final String ARG_TITLE_ID = "title_id";
public final static String ARG_POSITIVE_BTN_RES = "positive_btn_res";
public final static String ARG_NEUTRAL_BTN_RES = "neutral_btn_res";
public final static String ARG_NEGATIVE_BTN_RES = "negative_btn_res";
final static String ARG_POSITIVE_BTN_RES = "positive_btn_res";
final static String ARG_NEUTRAL_BTN_RES = "neutral_btn_res";
final static String ARG_NEGATIVE_BTN_RES = "negative_btn_res";
public static final String FTAG_CONFIRMATION = "CONFIRMATION_FRAGMENT";
@ -47,24 +45,16 @@ public class ConfirmationDialogFragment extends DialogFragment {
/**
* Public factory method to create new ConfirmationDialogFragment instances.
*
* @param messageResId Resource id for a message to show in the dialog.
* @param messageArguments Arguments to complete the message, if it's a format string. May be null.
* @param titleResId Resource id for a text to show in the title.
* 0 for default alert title, -1 for no title.
* @param posBtn Resource id for the text of the positive button. -1 for no positive button.
* @param neuBtn Resource id for the text of the neutral button. -1 for no neutral button.
* @param negBtn Resource id for the text of the negative button. -1 for no negative button.
* @return Dialog ready to show.
* @param messageResId Resource id for a message to show in the dialog.
* @param messageArguments Arguments to complete the message, if it's a format string. May be null.
* @param titleResId Resource id for a text to show in the title. 0 for default alert title, -1 for no title.
* @param posBtn Resource id for the text of the positive button. -1 for no positive button.
* @param neuBtn Resource id for the text of the neutral button. -1 for no neutral button.
* @param negBtn Resource id for the text of the negative button. -1 for no negative button.
* @return Dialog ready to show.
*/
public static ConfirmationDialogFragment newInstance(
int messageResId,
String[] messageArguments,
int titleResId,
int posBtn,
int neuBtn,
int negBtn
) {
public static ConfirmationDialogFragment newInstance(int messageResId, String[] messageArguments, int titleResId,
int posBtn, int neuBtn, int negBtn) {
if (messageResId == -1) {
throw new IllegalStateException("Calling confirmation dialog without message resource");
}
@ -81,27 +71,52 @@ public class ConfirmationDialogFragment extends DialogFragment {
return frag;
}
@Override
public void onStart() {
super.onStart();
int color = ThemeUtils.primaryAccentColor(getContext());
AlertDialog alertDialog = (AlertDialog) getDialog();
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(color);
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(color);
}
public void setOnConfirmationListener(ConfirmationDialogFragmentListener listener) {
mListener = listener;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Object[] messageArguments = getArguments().getStringArray(ARG_MESSAGE_ARGUMENTS);
int messageId = getArguments().getInt(ARG_MESSAGE_RESOURCE_ID, -1);
int titleId = getArguments().getInt(ARG_TITLE_ID, -1);
int posBtn = getArguments().getInt(ARG_POSITIVE_BTN_RES, -1);
int neuBtn = getArguments().getInt(ARG_NEUTRAL_BTN_RES, -1);
int negBtn = getArguments().getInt(ARG_NEGATIVE_BTN_RES, -1);
Bundle arguments = getArguments();
if (arguments == null) {
throw new IllegalArgumentException("Arguments may not be null");
}
Object[] messageArguments = arguments.getStringArray(ARG_MESSAGE_ARGUMENTS);
int messageId = arguments.getInt(ARG_MESSAGE_RESOURCE_ID, -1);
int titleId = arguments.getInt(ARG_TITLE_ID, -1);
int posBtn = arguments.getInt(ARG_POSITIVE_BTN_RES, -1);
int neuBtn = arguments.getInt(ARG_NEUTRAL_BTN_RES, -1);
int negBtn = arguments.getInt(ARG_NEGATIVE_BTN_RES, -1);
if (messageArguments == null) {
messageArguments = new String[]{};
}
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.Theme_ownCloud_Dialog)
.setIcon(R.drawable.ic_warning)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setMessage(String.format(getString(messageId), messageArguments));
Activity activity = getActivity();
if (activity == null) {
throw new IllegalArgumentException("Activity may not be null");
}
AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style.Theme_ownCloud_Dialog)
.setIcon(R.drawable.ic_warning)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setMessage(String.format(getString(messageId), messageArguments));
if (titleId == 0) {
builder.setTitle(android.R.string.dialog_alert_title);
@ -110,43 +125,32 @@ public class ConfirmationDialogFragment extends DialogFragment {
}
if (posBtn != -1) {
builder.setPositiveButton(posBtn,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
if (mListener != null) {
mListener.onConfirmation(getTag());
}
dialog.dismiss();
}
});
builder.setPositiveButton(posBtn, (dialog, whichButton) -> {
if (mListener != null) {
mListener.onConfirmation(getTag());
}
dialog.dismiss();
});
}
if (neuBtn != -1) {
builder.setNeutralButton(neuBtn,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
if (mListener != null) {
mListener.onNeutral(getTag());
}
dialog.dismiss();
}
});
builder.setNeutralButton(neuBtn, (dialog, whichButton) -> {
if (mListener != null) {
mListener.onNeutral(getTag());
}
dialog.dismiss();
});
}
if (negBtn != -1) {
builder.setNegativeButton(negBtn,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (mListener != null) {
mListener.onCancel(getTag());
}
dialog.dismiss();
}
});
builder.setNegativeButton(negBtn, (dialog, which) -> {
if (mListener != null) {
mListener.onCancel(getTag());
}
dialog.dismiss();
});
}
return builder.create();
return builder.create();
}
public interface ConfirmationDialogFragmentListener {
void onConfirmation(String callerTag);
@ -154,6 +158,5 @@ public class ConfirmationDialogFragment extends DialogFragment {
void onCancel(String callerTag);
}
}

View File

@ -54,4 +54,9 @@ public interface OCFileListBottomSheetActions {
* opens template selection for presentations
*/
void newPresentation();
/**
* offers direct camera upload to the current folder.
*/
void directCameraUpload();
}

View File

@ -47,7 +47,8 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog {
@BindView(R.id.menu_icon_upload_from_app)
public ImageView iconUploadFromApp;
@BindView(R.id.menu_icon_direct_camera_upload)
public ImageView iconDirectCameraUpload;
@BindView(R.id.menu_icon_mkdir)
public ImageView iconMakeDir;
@ -82,6 +83,7 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog {
int primaryColor = ThemeUtils.primaryColor(getContext(), true);
ThemeUtils.tintDrawable(iconUploadFiles.getDrawable(), primaryColor);
ThemeUtils.tintDrawable(iconUploadFromApp.getDrawable(), primaryColor);
ThemeUtils.tintDrawable(iconDirectCameraUpload.getDrawable(), primaryColor);
ThemeUtils.tintDrawable(iconMakeDir.getDrawable(), primaryColor);
headline.setText(getContext().getResources().getString(R.string.add_to_cloud,
@ -110,6 +112,12 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog {
dismiss();
}
@OnClick(R.id.menu_direct_camera_upload)
public void directCameraUpload() {
actions.directCameraUpload();
dismiss();
}
@OnClick(R.id.menu_upload_files)
public void uploadFiles() {
actions.uploadFiles();

View File

@ -417,6 +417,18 @@ public class OCFileListFragment extends ExtendedListFragment implements
);
}
@Override
public void directCameraUpload() {
FileDisplayActivity fileDisplayActivity = ((FileDisplayActivity) getActivity());
if (fileDisplayActivity != null) {
fileDisplayActivity.getFileOperationsHelper()
.uploadFromCamera(fileDisplayActivity, FileDisplayActivity.REQUEST_CODE__UPLOAD_FROM_CAMERA);
} else {
DisplayUtils.showSnackMessage(getView(), getString(R.string.error_starting_direct_camera_upload));
}
}
@Override
public void uploadFiles() {
UploadFilesActivity.startUploadActivityForResult(

View File

@ -24,6 +24,7 @@
package com.owncloud.android.ui.helpers;
import android.accounts.Account;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@ -32,6 +33,8 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.webkit.MimeTypeMap;
@ -77,10 +80,13 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -969,4 +975,46 @@ public class FileOperationsHelper {
mFileActivity.showLoadingDialog(mFileActivity.getString(R.string.wait_checking_credentials));
}
public void uploadFromCamera(Activity activity, int requestCode) {
Intent pictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
deleteOldFiles(activity);
File photoFile = createImageFile(activity);
Uri photoUri = FileProvider.getUriForFile(activity.getApplicationContext(),
activity.getResources().getString(R.string.file_provider_authority), photoFile);
pictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
if (pictureIntent.resolveActivity(activity.getPackageManager()) != null) {
activity.startActivityForResult(pictureIntent, requestCode);
} else {
DisplayUtils.showSnackMessage(activity, "No Camera found");
}
}
private void deleteOldFiles(Activity activity) {
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
if (storageDir != null) {
for (File file : storageDir.listFiles()) {
if (!file.delete()) {
Log_OC.d(this, "Failed to delete: " + file.getAbsolutePath());
}
}
}
}
public static File createImageFile(Activity activity) {
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return new File(storageDir + "/directCameraUpload.jpg");
}
public static String getCapturedImageName() {
return new SimpleDateFormat("Y-MM-dd_HHmmss", Locale.US).format(new Date()) + ".jpg";
}
}

View File

@ -0,0 +1,8 @@
<!-- drawable/camera.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M4,4H7L9,2H15L17,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20H4A2,2 0 0,1 2,18V6A2,2 0 0,1 4,4M12,7A5,5 0 0,0 7,12A5,5 0 0,0 12,17A5,5 0 0,0 17,12A5,5 0 0,0 12,7M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9Z" />
</vector>

View File

@ -35,10 +35,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="@dimen/standard_half_padding"
android:paddingLeft="@dimen/standard_padding"
android:paddingRight="@dimen/standard_padding"
android:paddingTop="@dimen/standard_half_padding"
android:paddingRight="@dimen/standard_padding"
android:paddingBottom="@dimen/standard_half_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
@ -53,8 +53,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/standard_margin"
android:layout_marginStart="@dimen/standard_margin"
android:layout_marginLeft="@dimen/standard_margin"
android:text="@string/upload_files"
android:textColor="@color/black"
android:textSize="@dimen/bottom_sheet_text_size"/>
@ -66,10 +66,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="@dimen/standard_half_padding"
android:paddingLeft="@dimen/standard_padding"
android:paddingRight="@dimen/standard_padding"
android:paddingTop="@dimen/standard_half_padding"
android:paddingRight="@dimen/standard_padding"
android:paddingBottom="@dimen/standard_half_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
@ -84,22 +84,63 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/standard_margin"
android:layout_marginStart="@dimen/standard_margin"
android:layout_marginLeft="@dimen/standard_margin"
android:text="@string/upload_content_from_other_apps"
android:textColor="@color/black"
android:textSize="@dimen/bottom_sheet_text_size"/>
</LinearLayout>
<View
android:id="@+id/dividerCameraUpload"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="@dimen/standard_margin"
android:layout_marginTop="@dimen/standard_half_margin"
android:layout_marginRight="@dimen/standard_margin"
android:layout_marginBottom="@dimen/standard_half_margin"
android:background="@color/list_divider_background" />
<LinearLayout
android:id="@+id/menu_direct_camera_upload"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="@dimen/standard_padding"
android:paddingTop="@dimen/standard_half_padding"
android:paddingRight="@dimen/standard_padding"
android:paddingBottom="@dimen/standard_half_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/menu_icon_direct_camera_upload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_camera"
android:tint="@color/primary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/standard_margin"
android:layout_marginLeft="@dimen/standard_margin"
android:text="@string/upload_direct_camera_upload"
android:textColor="@color/black"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="@dimen/standard_half_margin"
android:layout_marginLeft="@dimen/standard_margin"
android:layout_marginRight="@dimen/standard_margin"
android:layout_marginTop="@dimen/standard_half_margin"
android:layout_marginRight="@dimen/standard_margin"
android:layout_marginBottom="@dimen/standard_half_margin"
android:background="@color/list_divider_background"/>
<LinearLayout
@ -107,10 +148,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="@dimen/standard_padding"
android:paddingLeft="@dimen/standard_padding"
android:paddingRight="@dimen/standard_padding"
android:paddingTop="@dimen/standard_half_padding"
android:paddingRight="@dimen/standard_padding"
android:paddingBottom="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
@ -125,8 +166,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/standard_margin"
android:layout_marginStart="@dimen/standard_margin"
android:layout_marginLeft="@dimen/standard_margin"
android:text="@string/create_new_folder"
android:textColor="@color/black"
android:textSize="@dimen/bottom_sheet_text_size"/>

View File

@ -762,6 +762,7 @@
<string name="hint_password">Password</string>
<string name="add_to_cloud">Add to %1$s</string>
<string name="upload_files">Upload files</string>
<string name="upload_direct_camera_upload">Upload from camera</string>
<string name="upload_content_from_other_apps">Upload content from other apps</string>
<string name="create_new_folder">Create new folder</string>
<string name="uploads_view_upload_status_virus_detected">Virus detected. Upload cannot be completed!</string>
@ -845,6 +846,7 @@
<string name="battery_optimization_close">Close</string>
<string name="battery_optimization_no_setting">Unable to start battery settings directly. Please adjust manually in settings.</string>
<string name="file_details_no_content">Failed to load details</string>
<string name="error_starting_direct_camera_upload">Error starting camera</string>
<string name="uploader_upload_files_behaviour_not_writable">source folder is read-only; file will only be uploaded</string>
<string name="auto_upload_file_behaviour_kept_in_folder">kept in original folder, as it is readonly</string>
</resources>