diff --git a/app/build.gradle b/app/build.gradle index b47ae972..08113599 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "gr.thmmy.mthmmy" minSdkVersion 19 targetSdkVersion 28 - versionCode 21 - versionName "1.7.3" + versionCode 22 + versionName "1.7.4" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" 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 0b1195d2..98c23db9 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 @@ -40,7 +40,6 @@ import androidx.preference.PreferenceManager; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import net.gotev.uploadservice.MultipartUploadRequest; import net.gotev.uploadservice.UploadNotificationAction; import net.gotev.uploadservice.UploadNotificationConfig; @@ -67,6 +66,7 @@ import gr.thmmy.mthmmy.services.UploadsReceiver; import gr.thmmy.mthmmy.utils.AppCompatSpinnerWithoutDefault; import gr.thmmy.mthmmy.utils.FileUtils; import gr.thmmy.mthmmy.utils.TakePhoto; +import gr.thmmy.mthmmy.activities.upload.multipart.MultipartUploadRequest; import gr.thmmy.mthmmy.utils.parsing.ParseException; import gr.thmmy.mthmmy.utils.parsing.ParseTask; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; @@ -740,6 +740,7 @@ public class UploadActivity extends BaseActivity { .addFileToUpload(fileUri.toString() , "tp-dluploadfile") .addParameter("tp_dluploadicon", fileIcon) + .addParameter("tp_dluploadpic", "") .addParameter("tp-uploaduser", uploaderProfileIndex) .setNotificationConfig(uploadNotificationConfig) .setMaxRetries(2) diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java new file mode 100644 index 00000000..f6126523 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java @@ -0,0 +1,99 @@ +package gr.thmmy.mthmmy.activities.upload.multipart; + +import android.content.Context; +import android.content.Intent; + +import net.gotev.uploadservice.HttpUploadRequest; +import net.gotev.uploadservice.Logger; +import net.gotev.uploadservice.UploadFile; +import net.gotev.uploadservice.UploadTask; + +import java.io.FileNotFoundException; +import java.net.MalformedURLException; + +/** + From MultipartUploadRequest gotev/android-upload-service in order to use the local custom + MultipartUploadTask. + */ +public class MultipartUploadRequest extends HttpUploadRequest { + + private static final String LOG_TAG = MultipartUploadRequest.class.getSimpleName(); + private boolean isUtf8Charset = false; + + + public MultipartUploadRequest(final Context context, final String uploadId, final String serverUrl) + throws IllegalArgumentException, MalformedURLException { + super(context, uploadId, serverUrl); + } + + public MultipartUploadRequest(final Context context, final String serverUrl) + throws MalformedURLException, IllegalArgumentException { + this(context, null, serverUrl); + } + + @Override + protected void initializeIntent(Intent intent) { + super.initializeIntent(intent); + intent.putExtra(MultipartUploadTask.PARAM_UTF8_CHARSET, isUtf8Charset); + } + + @Override + protected Class getTaskClass() { + return MultipartUploadTask.class; + } + + public MultipartUploadRequest addFileToUpload(String filePath, + String parameterName, + String fileName, String contentType) + throws FileNotFoundException, IllegalArgumentException { + + UploadFile file = new UploadFile(filePath); + filePath = file.getPath(); + + if (parameterName == null || "".equals(parameterName)) { + throw new IllegalArgumentException("Please specify parameterName value for file: " + + filePath); + } + + file.setProperty(MultipartUploadTask.PROPERTY_PARAM_NAME, parameterName); + + if (contentType == null || contentType.isEmpty()) { + contentType = file.getContentType(context); + Logger.debug(LOG_TAG, "Auto-detected MIME type for " + filePath + + " is: " + contentType); + } else { + Logger.debug(LOG_TAG, "Content Type set for " + filePath + + " is: " + contentType); + } + + file.setProperty(MultipartUploadTask.PROPERTY_CONTENT_TYPE, contentType); + + if (fileName == null || "".equals(fileName)) { + fileName = file.getName(context); + Logger.debug(LOG_TAG, "Using original file name: " + fileName); + } else { + Logger.debug(LOG_TAG, "Using custom file name: " + fileName); + } + + file.setProperty(MultipartUploadTask.PROPERTY_REMOTE_FILE_NAME, fileName); + + params.files.add(file); + return this; + } + + public MultipartUploadRequest addFileToUpload(final String path, final String parameterName, + final String fileName) + throws FileNotFoundException, IllegalArgumentException { + return addFileToUpload(path, parameterName, fileName, null); + } + + public MultipartUploadRequest addFileToUpload(final String path, final String parameterName) + throws FileNotFoundException, IllegalArgumentException { + return addFileToUpload(path, parameterName, null, null); + } + + public MultipartUploadRequest setUtf8Charset() { + isUtf8Charset = true; + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java new file mode 100644 index 00000000..be9bb0f6 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java @@ -0,0 +1,160 @@ +package gr.thmmy.mthmmy.activities.upload.multipart; + +import android.content.Intent; + +import net.gotev.uploadservice.HttpUploadTask; +import net.gotev.uploadservice.NameValue; +import net.gotev.uploadservice.UploadFile; +import net.gotev.uploadservice.UploadService; +import net.gotev.uploadservice.http.BodyWriter; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +/** + Extended MultipartUploadTask from gotev/android-upload-service to include a fix for the parameter + tp_dluploadpic. Also changed Connection to keep-alive. + */ +public class MultipartUploadTask extends HttpUploadTask { + + static final String PARAM_UTF8_CHARSET = "multipartUtf8Charset"; + + private static final String BOUNDARY_SIGNATURE = "-------AndroidUploadService"; + private static final Charset US_ASCII = Charset.forName("US-ASCII"); + private static final String NEW_LINE = "\r\n"; + private static final String TWO_HYPHENS = "--"; + + // properties associated to each file + static final String PROPERTY_REMOTE_FILE_NAME = "httpRemoteFileName"; + static final String PROPERTY_CONTENT_TYPE = "httpContentType"; + static final String PROPERTY_PARAM_NAME = "httpParamName"; + + private byte[] boundaryBytes; + private byte[] trailerBytes; + private Charset charset; + + @Override + protected void init(UploadService service, Intent intent) throws IOException { + super.init(service, intent); + + String boundary = BOUNDARY_SIGNATURE + System.nanoTime(); + boundaryBytes = (TWO_HYPHENS + boundary + NEW_LINE).getBytes(US_ASCII); + trailerBytes = (TWO_HYPHENS + boundary + TWO_HYPHENS + NEW_LINE).getBytes(US_ASCII); + charset = intent.getBooleanExtra(PARAM_UTF8_CHARSET, false) ? + Charset.forName("UTF-8") : US_ASCII; + + httpParams.addHeader("Connection", "Keep-Alive"); + httpParams.addHeader("Content-Type", "multipart/form-data; boundary=" + boundary); + } + + @Override + protected long getBodyLength() throws UnsupportedEncodingException { + return (getRequestParametersLength() + getFilesLength() + trailerBytes.length); + } + + @Override + public void onBodyReady(BodyWriter bodyWriter) throws IOException { + //reset uploaded bytes when the body is ready to be written + //because sometimes this gets invoked when network changes + uploadedBytes = 0; + writeRequestParameters(bodyWriter); + writeFiles(bodyWriter); + bodyWriter.write(trailerBytes); + uploadedBytes += trailerBytes.length; + broadcastProgress(uploadedBytes, totalBytes); + } + + private long getFilesLength() throws UnsupportedEncodingException { + long total = 0; + + for (UploadFile file : params.files) { + total += getTotalMultipartBytes(file); + } + + return total; + } + + private long getRequestParametersLength() throws UnsupportedEncodingException { + long parametersBytes = 0; + + if (!httpParams.getRequestParameters().isEmpty()) { + for (final NameValue parameter : httpParams.getRequestParameters()) { + // the bytes needed for every parameter are the sum of the boundary bytes + // and the bytes occupied by the parameter + parametersBytes += boundaryBytes.length + getMultipartBytes(parameter).length; + } + } + + return parametersBytes; + } + + private byte[] getMultipartBytes(NameValue parameter) throws UnsupportedEncodingException { + if(parameter.getName().equals("tp_dluploadpic")){ + String header = "Content-Disposition: form-data; name=\"" + + parameter.getName() + "\"; filename=\"\"" + NEW_LINE + + "Content-Type: application/octet-stream" + NEW_LINE + NEW_LINE; + return header.getBytes(charset); + } + else + return ("Content-Disposition: form-data; name=\"" + parameter.getName() + "\"" + + NEW_LINE + NEW_LINE + parameter.getValue() + NEW_LINE).getBytes(charset); + } + + private byte[] getMultipartHeader(UploadFile file) + throws UnsupportedEncodingException { + String header = "Content-Disposition: form-data; name=\"" + + file.getProperty(PROPERTY_PARAM_NAME) + "\"; filename=\"" + + file.getProperty(PROPERTY_REMOTE_FILE_NAME) + "\"" + NEW_LINE + + "Content-Type: " + file.getProperty(PROPERTY_CONTENT_TYPE) + + NEW_LINE + NEW_LINE; + + return header.getBytes(charset); + } + + private long getTotalMultipartBytes(UploadFile file) + throws UnsupportedEncodingException { + return boundaryBytes.length + getMultipartHeader(file).length + file.length(service) + + NEW_LINE.getBytes(charset).length; + } + + private void writeRequestParameters(BodyWriter bodyWriter) throws IOException { + if (!httpParams.getRequestParameters().isEmpty()) { + for (final NameValue parameter : httpParams.getRequestParameters()) { + bodyWriter.write(boundaryBytes); + byte[] formItemBytes = getMultipartBytes(parameter); + bodyWriter.write(formItemBytes); + + uploadedBytes += boundaryBytes.length + formItemBytes.length; + broadcastProgress(uploadedBytes, totalBytes); + } + } + } + + private void writeFiles(BodyWriter bodyWriter) throws IOException { + for (UploadFile file : params.files) { + if (!shouldContinue) + break; + + bodyWriter.write(boundaryBytes); + byte[] headerBytes = getMultipartHeader(file); + bodyWriter.write(headerBytes); + + uploadedBytes += boundaryBytes.length + headerBytes.length; + broadcastProgress(uploadedBytes, totalBytes); + + bodyWriter.writeStream(file.getStream(service), this); + + byte[] newLineBytes = NEW_LINE.getBytes(charset); + bodyWriter.write(newLineBytes); + uploadedBytes += newLineBytes.length; + } + } + + @Override + protected void onSuccessfulUpload() { + addAllFilesToSuccessfullyUploadedFiles(); + } + +} +