diff --git a/app/build.gradle b/app/build.gradle index 00e58bbd..18093fa6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,6 +46,7 @@ dependencies { implementation 'com.google.firebase:firebase-core:16.0.3' implementation 'com.google.firebase:firebase-messaging:17.3.0' implementation 'com.crashlytics.sdk.android:crashlytics:2.9.5' + implementation 'com.snatik:storage:2.1.0' implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.squareup.picasso:picasso:2.5.2' implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java index 26ab4222..1a0be773 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java @@ -5,16 +5,21 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.database.Cursor; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Environment; +import android.provider.OpenableColumns; import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.FileProvider; import android.support.v7.content.res.AppCompatResources; import android.support.v7.preference.PreferenceManager; import android.support.v7.widget.AppCompatButton; import android.support.v7.widget.AppCompatTextView; +import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -23,6 +28,9 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.Toast; + +import com.snatik.storage.Storage; + import net.gotev.uploadservice.MultipartUploadRequest; import net.gotev.uploadservice.ServerResponse; import net.gotev.uploadservice.UploadInfo; @@ -74,15 +82,25 @@ public class UploadActivity extends BaseActivity { */ private static final int UPLOAD_REQUEST_CODE = 42; //Arbitrary, application specific + private static final int NO_UPLOAD_METHOD_SELECTED = 0; + private static final int SELECT_FILE_METHOD_SELECTED = 1; + private static final int TAKE_PHOTO_METHOD_SELECTED = 2; + + private static final String TEMPORARY_FILES_DIRECTORY = "~tmp_mThmmy_uploads"; + private static final int MAX_FILE_SIZE_SUPPORTED = 45000000; + private ArrayList uploadRootCategories = new ArrayList<>(); private ParseUploadPageTask parseUploadPageTask; private ArrayList bundleCategory; private String categorySelected = "-1"; private String uploaderProfileIndex = "1"; - private String uploadFilename; + + private int uploadMethodSelected = NO_UPLOAD_METHOD_SELECTED; private Uri fileUri; + private File photoFileSelected = null; + private File photoFileCreated = null; private String fileIcon; - private File photoFile = null; + private String generatorUploadFilename; //UI elements private MaterialProgressBar progressBar; @@ -221,48 +239,80 @@ public class UploadActivity extends BaseActivity { Drawable takePhotoDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_photo_camera_white_24dp); takePhotoButton.setCompoundDrawablesRelativeWithIntrinsicBounds(takePhotoDrawable, null, null, null); takePhotoButton.setOnClickListener(v -> { - if(checkPerms()) - takePhoto(); - else - requestPerms(UPLOAD_REQUEST_CODE); + if (checkPerms()) + takePhoto(); + else + requestPerms(UPLOAD_REQUEST_CODE); }); FloatingActionButton uploadFAB = findViewById(R.id.upload_fab); uploadFAB.setOnClickListener(view -> { + //Attempts upload progressBar.setVisibility(View.VISIBLE); String uploadTitleText = uploadTitle.getText().toString(); String uploadDescriptionText = uploadDescription.getText().toString(); - if (uploadTitleText.equals("")) { - uploadTitle.setError("Required"); - } - if (fileUri == null) { - Toast.makeText(view.getContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show(); - } - if (categorySelected.equals("-1")) { - Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show(); - } - - if (categorySelected.equals("-1") || uploadTitleText.equals("") || fileUri == null) { - progressBar.setVisibility(View.GONE); - return; + //Checks if all required fields are filled + { + boolean shouldReturn = false; + if (uploadTitleText.equals("")) { + uploadTitle.setError("Required"); + shouldReturn = true; + } + if (fileUri == null) { + Toast.makeText(view.getContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show(); + shouldReturn = true; + } + if (categorySelected.equals("-1")) { + Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show(); + shouldReturn = true; + } + if (UploadsHelper.sizeFromUri(this, fileUri) > MAX_FILE_SIZE_SUPPORTED) { + Toast.makeText(view.getContext(), "Your files are too powerful for thmmy. Reduce size or split!", Toast.LENGTH_LONG).show(); + shouldReturn = true; + } + if (shouldReturn) { + progressBar.setVisibility(View.GONE); + return; + } } + //Checks settings and adds "Uploaded from mTHMMY" string to description SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(view.getContext()); if (sharedPrefs.getBoolean(UPLOADING_APP_SIGNATURE_ENABLE_KEY, true)) { uploadDescriptionText += uploadedFromThmmyPromptHtml; } - String tempFilePath = null; - if (uploadFilename != null) { - //File should be uploaded with a certain name. Temporarily copies the file and renames it - tempFilePath = UploadsHelper.createTempFile(this, fileUri, uploadFilename); - if (tempFilePath == null) { - //Something went wrong, abort - Toast.makeText(this, "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show(); - progressBar.setVisibility(View.GONE); - return; + //Checks if the generator was used + if (generatorUploadFilename != null) { + //File should be uploaded with a certain name. + switch (uploadMethodSelected) { + case SELECT_FILE_METHOD_SELECTED: + //Temporarily copies the file to a another location and renames it + fileUri = UploadsHelper.createTempFile(this, storage, fileUri, + generatorUploadFilename); + break; + case TAKE_PHOTO_METHOD_SELECTED: + //Renames the photo taken + String photoPath = photoFileSelected.getPath(); + photoPath = photoPath.substring(0, photoPath.lastIndexOf(File.separator)); + String destinationFilename = photoPath + File.separator + generatorUploadFilename + ".jpg"; + + if (!storage.rename(photoFileSelected.getAbsolutePath(), destinationFilename)) { + //Something went wrong, abort + Toast.makeText(this, "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show(); + progressBar.setVisibility(View.GONE); + return; + } + + //Points photoFile and fileUri to the new copied and renamed file + photoFileSelected = storage.getFile(destinationFilename); + fileUri = FileProvider.getUriForFile(this, getPackageName() + + ".provider", photoFileSelected); + break; + default: + break; } } @@ -272,10 +322,7 @@ public class UploadActivity extends BaseActivity { .addParameter("tp-dluploadtitle", uploadTitleText) .addParameter("tp-dluploadcat", categorySelected) .addParameter("tp-dluploadtext", uploadDescriptionText) - .addFileToUpload(tempFilePath == null - ? fileUri.toString() - : tempFilePath - , "tp-dluploadfile") + .addFileToUpload(fileUri.toString(), "tp-dluploadfile") .addParameter("tp_dluploadicon", fileIcon) .addParameter("tp-uploaduser", uploaderProfileIndex) .setNotificationConfig(new UploadNotificationConfig()) @@ -299,9 +346,15 @@ public class UploadActivity extends BaseActivity { UploadsHelper.deleteTempFiles(); BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null); + + if (uploadMethodSelected == TAKE_PHOTO_METHOD_SELECTED) { + TakePhoto.galleryAddPic(context, photoFileSelected); + } uploadTitle.setText(null); uploadDescription.setText(null); fileUri = null; + photoFileCreated = null; + photoFileSelected = null; filenameHolder.setText(null); filenameHolder.setVisibility(View.GONE); progressBar.setVisibility(View.GONE); @@ -353,6 +406,14 @@ public class UploadActivity extends BaseActivity { super.onDestroy(); if (parseUploadPageTask != null && parseUploadPageTask.getStatus() != AsyncTask.Status.RUNNING) parseUploadPageTask.cancel(true); + + //Deletes any photo file previously created, as it is not going to be used + if (photoFileCreated != null) { + storage.deleteFile(photoFileCreated.getAbsolutePath()); + } + if (photoFileSelected != null) { + storage.deleteFile(photoFileSelected.getAbsolutePath()); + } } @Override @@ -362,6 +423,15 @@ public class UploadActivity extends BaseActivity { return; } + //Deletes any photo file previously created, as it is not going to be used + if (photoFileCreated != null) { + storage.deleteFile(photoFileCreated.getAbsolutePath()); + } + if (photoFileSelected != null) { + storage.deleteFile(photoFileSelected.getAbsolutePath()); + } + uploadMethodSelected = SELECT_FILE_METHOD_SELECTED; + fileUri = data.getData(); if (fileUri != null) { String filename = UploadsHelper.filenameFromUri(this, fileUri); @@ -390,12 +460,20 @@ public class UploadActivity extends BaseActivity { } } else if (requestCode == AFR_REQUEST_CODE_CAMERA) { if (resultCode == Activity.RESULT_CANCELED) { + //Deletes image file + storage.deleteFile(photoFileCreated.getAbsolutePath()); return; } + uploadMethodSelected = TAKE_PHOTO_METHOD_SELECTED; + + if (photoFileSelected != null) { + storage.deleteFile(photoFileSelected.getAbsolutePath()); + } + photoFileSelected = photoFileCreated; - fileUri = TakePhoto.processResult(this, photoFile); + fileUri = TakePhoto.processResult(this, photoFileSelected); - filenameHolder.setText(photoFile.getName()); + filenameHolder.setText(photoFileSelected.getName()); filenameHolder.setVisibility(View.VISIBLE); fileIcon = "jpg_image.gif"; } else if (requestCode == AFR_REQUEST_CODE_FIELDS_BUILDER) { @@ -403,7 +481,7 @@ public class UploadActivity extends BaseActivity { return; } - uploadFilename = data.getStringExtra(RESULT_FILENAME); + generatorUploadFilename = data.getStringExtra(RESULT_FILENAME); uploadTitle.setText(data.getStringExtra(RESULT_TITLE)); uploadDescription.setText(data.getStringExtra(RESULT_DESCRIPTION)); } else { @@ -423,13 +501,15 @@ public class UploadActivity extends BaseActivity { } // Should only be called after making sure permissions are granted - private void takePhoto(){ + private void takePhoto() { // Create the File where the photo should go - photoFile = TakePhoto.createImageFile(this); + photoFileCreated = TakePhoto.createImageFile(this); // Continue only if the File was successfully created - if (photoFile != null) - startActivityForResult(TakePhoto.getIntent(this, photoFile), AFR_REQUEST_CODE_CAMERA); + if (photoFileCreated != null) { + startActivityForResult(TakePhoto.getIntent(this, photoFileCreated), + AFR_REQUEST_CODE_CAMERA); + } } private class CustomOnItemSelectedListener implements AdapterView.OnItemSelectedListener { diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java index 35dd0590..f8724a89 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java @@ -1,13 +1,7 @@ package gr.thmmy.mthmmy.activities.upload; import android.content.Context; -import android.content.Intent; -import android.content.res.AssetFileDescriptor; import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; -import android.media.ExifInterface; import android.net.Uri; import android.os.Environment; import android.provider.OpenableColumns; @@ -16,46 +10,21 @@ import android.support.annotation.Nullable; import android.support.v4.content.FileProvider; import android.widget.Toast; +import com.snatik.storage.Storage; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; import timber.log.Timber; -import static android.support.v4.content.FileProvider.getUriForFile; - class UploadsHelper { - @NonNull - static String filenameFromUri(Context context, Uri uri) { - String filename = null; - if (uri.getScheme().equals("content")) { - try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { - if (cursor != null && cursor.moveToFirst()) { - filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); - } - } - } - if (filename == null) { - filename = uri.getPath(); - int cut = filename.lastIndexOf('/'); - if (cut != -1) { - filename = filename.substring(cut + 1); - } - } - - return filename; - } - @SuppressWarnings("ResultOfMethodCallIgnored") @Nullable - static String createTempFile(Context context, Uri fileUri, String newFilename) { + static Uri createTempFile(Context context, Storage storage, Uri fileUri, String newFilename) { String oldFilename = filenameFromUri(context, fileUri); String fileExtension = oldFilename.substring(oldFilename.indexOf(".")); String destinationFilename = Environment.getExternalStorageDirectory().getPath() + @@ -101,7 +70,38 @@ class UploadsHelper { } } - return destinationFilename; + return FileProvider.getUriForFile(context, context.getPackageName() + + ".provider", storage.getFile(destinationFilename)); + } + + @NonNull + static String filenameFromUri(Context context, Uri uri) { + String filename = null; + if (uri.getScheme().equals("content")) { + try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { + if (cursor != null && cursor.moveToFirst()) { + filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); + } + } + } + if (filename == null) { + filename = uri.getPath(); + int cut = filename.lastIndexOf('/'); + if (cut != -1) { + filename = filename.substring(cut + 1); + } + } + + return filename; + } + + static long sizeFromUri(Context context, Uri uri){ + try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { + if (cursor != null && cursor.moveToFirst()) { + return cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE)); + } + } + return -1; } @SuppressWarnings("ResultOfMethodCallIgnored") diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java index 67c2322b..4fdd7e43 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java @@ -37,6 +37,7 @@ import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.ProfileDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IProfile; +import com.snatik.storage.Storage; import java.io.File; import java.util.ArrayList; @@ -90,6 +91,7 @@ public abstract class BaseActivity extends AppCompatActivity { //Common UI elements protected Toolbar toolbar; protected Drawer drawer; + protected Storage storage; private MainActivity mainActivity; @@ -112,6 +114,8 @@ public abstract class BaseActivity extends AppCompatActivity { baseViewModel.getCurrentPageBookmark().observe(this, thisPageBookmark -> { setTopicBookmark(thisPageBookmarkMenuButton); }); + + storage = new Storage(getApplicationContext()); } @Override diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java b/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java index 0565d742..967b523f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java @@ -11,12 +11,14 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.media.ExifInterface; +import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.content.FileProvider; +import android.util.Log; import android.widget.Toast; import java.io.File; @@ -31,6 +33,7 @@ import timber.log.Timber; public class TakePhoto { private static final int DEFAULT_MIN_WIDTH_QUALITY = 400; + private static final String IMAGE_CONTENT_DESCRIPTION = "mThmmy uploads image"; @Nullable public static Intent getIntent(Context context, @NonNull File photoFile) { @@ -92,17 +95,14 @@ public class TakePhoto { } } - File photoFile = new File(imageFolder, imageFileName); - galleryAddPic(context, photoFile); - - return photoFile; + return new File(imageFolder, imageFileName); } - private static void galleryAddPic(Context context, File photoFile) { + public static void galleryAddPic(Context context, File photoFile) { ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.TITLE, photoFile.getName()); - values.put(MediaStore.Images.Media.DESCRIPTION, "mThmmy uploads image"); - values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()); //TODO fix bug + values.put(MediaStore.Images.Media.DESCRIPTION, IMAGE_CONTENT_DESCRIPTION); + values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()); values.put(MediaStore.Images.ImageColumns.BUCKET_ID, photoFile.toString().toLowerCase(Locale.US).hashCode()); values.put(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, photoFile.getName().toLowerCase(Locale.US)); values.put("_data", photoFile.getAbsolutePath());