Direct camera upload
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de> Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
parent
6ff68d52e8
commit
8ae93db343
|
@ -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 |
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -54,4 +54,9 @@ public interface OCFileListBottomSheetActions {
|
|||
* opens template selection for presentations
|
||||
*/
|
||||
void newPresentation();
|
||||
|
||||
/**
|
||||
* offers direct camera upload to the current folder.
|
||||
*/
|
||||
void directCameraUpload();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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"/>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue