diff options
author | desperateCoder <echotodevnull@gmail.com> | 2020-02-02 01:02:30 +0300 |
---|---|---|
committer | desperateCoder <echotodevnull@gmail.com> | 2020-02-02 01:02:30 +0300 |
commit | 8191eff363b0edd9a3e7798783e26a1c2e896b82 (patch) | |
tree | 0df44dfaf14d8c87ddab3dd5e8fe0ce3adb240c2 | |
parent | 08530e19c307ecfd3cb401b10a4ed21e2e15c50f (diff) |
attachments upload: something does something
5 files changed, 386 insertions, 19 deletions
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java index 2a8ddab03..1c678687f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java @@ -13,6 +13,7 @@ import androidx.lifecycle.MutableLiveData; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; +import java.io.File; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; @@ -964,18 +965,13 @@ public class SyncManager { } } - public LiveData<Attachment> addAttachmentToCard(long accountId, long localCardId, @NonNull Uri uri) { - try { - DeckLog.log(getPath(dataBaseAdapter.getContext(), uri)); - } catch (URISyntaxException e) { - e.printStackTrace(); - } + public LiveData<Attachment> addAttachmentToCard(long accountId, long localCardId, @NonNull String mimeType, @NonNull File file) { MutableLiveData<Attachment> liveData = new MutableLiveData<>(); doAsync(() -> { Attachment attachment = new Attachment(); attachment.setCardId(localCardId); - attachment.setMimetype(Attachment.getMimetypeForUri(dataBaseAdapter.getContext(), uri)); - attachment.setLocalPath(uri.getPath()); + attachment.setMimetype(mimeType); + attachment.setLocalPath(file.getAbsolutePath()); attachment.setCreatedAt(new Date()); dataBaseAdapter.createAttachment(accountId, attachment); if (serverAdapter.hasInternetConnection()) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java index 7ddf8fa9a..5ab72e99b 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java @@ -46,7 +46,6 @@ import it.niedermann.nextcloud.deck.model.full.FullStack; import it.niedermann.nextcloud.deck.model.ocs.Capabilities; import it.niedermann.nextcloud.deck.model.propagation.CardUpdate; import it.niedermann.nextcloud.deck.model.propagation.Reorder; -import it.niedermann.nextcloud.deck.persistence.util.RealPathUtils; import it.niedermann.nextcloud.deck.util.DateUtil; import okhttp3.MediaType; import okhttp3.MultipartBody; @@ -279,10 +278,8 @@ public class ServerAdapter { } // ## ATTACHMENTS - public void uploadAttachment(Long remoteBoardId, long remoteStackId, long remoteCardId, String contentType, Uri attachmentUri, IResponseCallback<Attachment> responseCallback) { + public void uploadAttachment(Long remoteBoardId, long remoteStackId, long remoteCardId, String contentType, File attachment, IResponseCallback<Attachment> responseCallback) { ensureInternetConnection(); -// File attachment = new File(getUriRealPath( applicationContext, attachmentUri)); - File attachment = RealPathUtils.getRealPath(applicationContext, attachmentUri); MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", attachment.getName(), RequestBody.create(MediaType.parse(contentType), attachment)); MultipartBody.Part typePart = MultipartBody.Part.createFormData("type", attachment.getName(), RequestBody.create(MediaType.parse("text/plain"), "deck_file")); RequestHelper.request(sourceActivity, provider, () -> provider.getDeckAPI().uploadAttachment(remoteBoardId, remoteStackId, remoteCardId, typePart, filePart), responseCallback); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/AttachmentDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/AttachmentDataProvider.java index 4a9688b3c..6869d7fe5 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/AttachmentDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/AttachmentDataProvider.java @@ -64,9 +64,7 @@ public class AttachmentDataProvider extends AbstractSyncDataProvider<Attachment> @Override public void createOnServer(ServerAdapter serverAdapter, DataBaseAdapter dataBaseAdapter, long accountId, IResponseCallback<Attachment> responder, Attachment entity) { - Uri uri = Uri.fromFile(new File(entity.getLocalPath())); - String type = Attachment.getMimetypeForUri(dataBaseAdapter.getContext(), uri); - serverAdapter.uploadAttachment(board.getId(), stack.getId(), card.getId(), type, uri, responder); + serverAdapter.uploadAttachment(board.getId(), stack.getId(), card.getId(), entity.getType(), new File(entity.getLocalPath()), responder); } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAttachmentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAttachmentsFragment.java index a7410e587..10d56fe31 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAttachmentsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAttachmentsFragment.java @@ -15,12 +15,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.core.app.ActivityCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import java.io.File; import java.util.Objects; import butterknife.BindView; @@ -31,6 +31,7 @@ import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Attachment; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.ui.helper.emptycontentview.EmptyContentView; +import it.niedermann.nextcloud.deck.util.FileUtils; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_ACCOUNT_ID; @@ -91,7 +92,7 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentAdapt if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && canEdit) { fab.setOnClickListener(v -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, + requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION); } else { startFilePickerIntent(); @@ -120,7 +121,7 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentAdapt @RequiresApi(api = Build.VERSION_CODES.KITKAT) private void startFilePickerIntent() { // Snackbar.make(coordinatorLayout, "Adding attachments is not yet implemented", Snackbar.LENGTH_LONG).show(); - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); startActivityForResult(intent, REQUEST_CODE_ADD_ATTACHMENT); @@ -134,7 +135,9 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentAdapt if (data != null) { uri = data.getData(); Log.i(TAG, "Uri: " + uri.toString()); - syncManager.addAttachmentToCard(accountId, cardId, uri); + String path = FileUtils.getPath(getContext(), uri); + File uploadFile = new File(path); + syncManager.addAttachmentToCard(accountId, cardId, Attachment.getMimetypeForUri(getContext(), uri), uploadFile); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/util/FileUtils.java b/app/src/main/java/it/niedermann/nextcloud/deck/util/FileUtils.java new file mode 100644 index 000000000..6dd015692 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/util/FileUtils.java @@ -0,0 +1,373 @@ +package it.niedermann.nextcloud.deck.util; + +import android.annotation.SuppressLint; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; +import android.provider.OpenableColumns; +import android.text.TextUtils; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; + +public class FileUtils { + + /** + * Get a file path from a Uri. This will get the the path for Storage Access + * Framework Documents, as well as the _data field for the MediaStore and + * other file-based ContentProviders.<br> + * <br> + * Callers should check whether the path is local before assuming it + * represents a local file. + * + * @param context The context. + * @param uri The Uri to query. + */ + @SuppressLint("NewApi") + public static String getPath(final Context context, final Uri uri) { + // check here to KITKAT or new version + final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + String selection = null; + String[] selectionArgs = null; + // DocumentProvider + if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { + // ExternalStorageProvider + if (isExternalStorageDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + String fullPath = getPathFromExtSD(split); + if (fullPath != "") { + return fullPath; + } else { + return null; + } + } + + // DownloadsProvider + else if (isDownloadsDocument(uri)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + final String id; + Cursor cursor = null; + try { + cursor = context.getContentResolver().query(uri, new String[]{MediaStore.MediaColumns.DISPLAY_NAME}, null, null, null); + if (cursor != null && cursor.moveToFirst()) { + String fileName = cursor.getString(0); + String path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName; + if (!TextUtils.isEmpty(path)) { + return path; + } + } + } finally { + if (cursor != null) + cursor.close(); + } + id = DocumentsContract.getDocumentId(uri); + if (!TextUtils.isEmpty(id)) { + if (id.startsWith("raw:")) { + return id.replaceFirst("raw:", ""); + } + String[] contentUriPrefixesToTry = new String[]{ + "content://downloads/public_downloads", + "content://downloads/my_downloads" + }; + for (String contentUriPrefix : contentUriPrefixesToTry) { + try { + final Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id)); + + /* final Uri contentUri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));*/ + + return getDataColumn(context, contentUri, null, null); + } catch (NumberFormatException e) { + //In Android 8 and Android P the id is not a number + return uri.getPath().replaceFirst("^/document/raw:", "").replaceFirst("^raw:", ""); + } + } + + + } + + } else { + final String id = DocumentsContract.getDocumentId(uri); + final boolean isOreo = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + if (id.startsWith("raw:")) { + return id.replaceFirst("raw:", ""); + } + Uri contentUri = null; + try { + contentUri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + + } catch (NumberFormatException e) { + e.printStackTrace(); + } + if (contentUri != null) { + return getDataColumn(context, contentUri, null, null); + } + } + + + } + // MediaProvider + else if (isMediaDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + Uri contentUri = null; + + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else if ("video".equals(type)) { + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } else if ("audio".equals(type)) { + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } + selection = "_id=?"; + selectionArgs = new String[]{split[1]}; + + + return getDataColumn(context, contentUri, selection, + selectionArgs); + } else if (isGoogleDriveUri(uri)) { + return getDriveFilePath(uri, context); + } + } + + + // MediaStore (and general) + else if ("content".equalsIgnoreCase(uri.getScheme())) { + + if (isGooglePhotosUri(uri)) { + return uri.getLastPathSegment(); + } + + if (isGoogleDriveUri(uri)) { + return getDriveFilePath(uri, context); + } + if( Build.VERSION.SDK_INT == Build.VERSION_CODES.N) + { + // return getFilePathFromURI(context,uri); + return getMediaFilePathForN(uri, context); + // return getRealPathFromURI(context,uri); + }else + { + + return getDataColumn(context, uri, null, null); + } + + + } + // File + else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + + return null; + } + + /** + * Check if a file exists on device + * + * @param filePath The absolute file path + */ + private static boolean fileExists(String filePath) { + File file = new File(filePath); + + return file.exists(); + } + + + /** + * Get full file path from external storage + * + * @param pathData The storage type and the relative path + */ + private static String getPathFromExtSD(String[] pathData) { + final String type = pathData[0]; + final String relativePath = "/" + pathData[1]; + String fullPath = ""; + + // on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string + // something like "71F8-2C0A", some kind of unique id per storage + // don't know any API that can get the root path of that storage based on its id. + // + // so no "primary" type, but let the check here for other devices + if ("primary".equalsIgnoreCase(type)) { + fullPath = Environment.getExternalStorageDirectory() + relativePath; + if (fileExists(fullPath)) { + return fullPath; + } + } + + // Environment.isExternalStorageRemovable() is `true` for external and internal storage + // so we cannot relay on it. + // + // instead, for each possible path, check if file exists + // we'll start with secondary storage as this could be our (physically) removable sd card + fullPath = System.getenv("SECONDARY_STORAGE") + relativePath; + if (fileExists(fullPath)) { + return fullPath; + } + + fullPath = System.getenv("EXTERNAL_STORAGE") + relativePath; + if (fileExists(fullPath)) { + return fullPath; + } + + return fullPath; + } + + private static String getDriveFilePath(Uri uri, Context context) { + Uri returnUri = uri; + Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null); + /* + * Get the column indexes of the data in the Cursor, + * * move to the first row in the Cursor, get the data, + * * and display it. + * */ + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); + returnCursor.moveToFirst(); + String name = (returnCursor.getString(nameIndex)); + String size = (Long.toString(returnCursor.getLong(sizeIndex))); + File file = new File(context.getCacheDir(), name); + try { + InputStream inputStream = context.getContentResolver().openInputStream(uri); + FileOutputStream outputStream = new FileOutputStream(file); + int read = 0; + int maxBufferSize = 1 * 1024 * 1024; + int bytesAvailable = inputStream.available(); + + //int bufferSize = 1024; + int bufferSize = Math.min(bytesAvailable, maxBufferSize); + + final byte[] buffers = new byte[bufferSize]; + while ((read = inputStream.read(buffers)) != -1) { + outputStream.write(buffers, 0, read); + } + Log.e("File Size", "Size " + file.length()); + inputStream.close(); + outputStream.close(); + Log.e("File Path", "Path " + file.getPath()); + Log.e("File Size", "Size " + file.length()); + } catch (Exception e) { + Log.e("Exception", e.getMessage()); + } + return file.getPath(); + } + + private static String getMediaFilePathForN(Uri uri, Context context) { + Uri returnUri = uri; + Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null); + /* + * Get the column indexes of the data in the Cursor, + * * move to the first row in the Cursor, get the data, + * * and display it. + * */ + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); + returnCursor.moveToFirst(); + String name = (returnCursor.getString(nameIndex)); + String size = (Long.toString(returnCursor.getLong(sizeIndex))); + File file = new File(context.getFilesDir(), name); + try { + InputStream inputStream = context.getContentResolver().openInputStream(uri); + FileOutputStream outputStream = new FileOutputStream(file); + int read = 0; + int maxBufferSize = 1 * 1024 * 1024; + int bytesAvailable = inputStream.available(); + + //int bufferSize = 1024; + int bufferSize = Math.min(bytesAvailable, maxBufferSize); + + final byte[] buffers = new byte[bufferSize]; + while ((read = inputStream.read(buffers)) != -1) { + outputStream.write(buffers, 0, read); + } + Log.e("File Size", "Size " + file.length()); + inputStream.close(); + outputStream.close(); + Log.e("File Path", "Path " + file.getPath()); + Log.e("File Size", "Size " + file.length()); + } catch (Exception e) { + Log.e("Exception", e.getMessage()); + } + return file.getPath(); + } + + + private static String getDataColumn(Context context, Uri uri, + String selection, String[] selectionArgs) { + Cursor cursor = null; + final String column = "_data"; + final String[] projection = {column}; + + try { + cursor = context.getContentResolver().query(uri, projection, + selection, selectionArgs, null); + + if (cursor != null && cursor.moveToFirst()) { + final int index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(index); + } + } finally { + if (cursor != null) + cursor.close(); + } + + return null; + } + + /** + * @param uri - The Uri to check. + * @return - Whether the Uri authority is ExternalStorageProvider. + */ + private static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + /** + * @param uri - The Uri to check. + * @return - Whether the Uri authority is DownloadsProvider. + */ + private static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + /** + * @param uri - The Uri to check. + * @return - Whether the Uri authority is MediaProvider. + */ + private static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + /** + * @param uri - The Uri to check. + * @return - Whether the Uri authority is Google Photos. + */ + private static boolean isGooglePhotosUri(Uri uri) { + return "com.google.android.apps.photos.content".equals(uri.getAuthority()); + } + + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is Google Drive. + */ + private static boolean isGoogleDriveUri(Uri uri) { + return "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority()); + } + + +}
\ No newline at end of file |